diff --git a/.gitignore b/.gitignore
index 08f1c3b25..d28292a8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ dist/
dist2/
dist2.7/
dist3/
+dist-react/
allDist/
packages/**/runtime
coverage/
@@ -14,6 +15,11 @@ coverage/
/packages/vue/mobile-first.ts
/packages/vue/app.ts
+/packages/react/index.ts
+/packages/react/pc.ts
+/packages/react/mobile.ts
+/packages/react/app.ts
+
/examples/**/playwright-report
vite.config.ts.timestamp*
vitest.config.ts.timestamp*
@@ -47,5 +53,3 @@ packages/theme/scripts/theme-result.txt
packages/theme/scripts/themeExcel.xlsx
packages/theme/src/theme/*-theme/component.js
-
-
diff --git a/examples/docs/newsrc/uses/useMonaco.js b/examples/docs/newsrc/uses/useMonaco.js
new file mode 100644
index 000000000..fb376012d
--- /dev/null
+++ b/examples/docs/newsrc/uses/useMonaco.js
@@ -0,0 +1,31 @@
+import * as monaco from 'monaco-editor'
+import { hooks } from '@opentiny/vue-common'
+// monaco ESM模块集成说明 : https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-esm.md#using-vite
+// https://github.com/vitejs/vite/discussions/1791#discussioncomment-321046
+import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
+self.MonacoEnvironment = {
+ getWorker: () => new HtmlWorker()
+}
+
+export function useMonaco(selector) {
+ const state = {
+ editor: null,
+ getCode: () => state.editor && state.editor.getValue(),
+ setCode: (code) => state.editor && state.editor.setValue(code),
+ hotKey: (key, fn) => state.editor && state.editor.addCommand(key, fn),
+ format: () => state.editor && state.editor.trigger('anyString', 'editor.action.formatDocument'),
+ scrollTop: () => state.editor && state.editor.setScrollTop(0)
+ }
+
+ hooks.onMounted(() => {
+ state.editor = monaco.editor.create(document.querySelector(selector), {
+ value: '',
+ language: 'html',
+ theme: 'vs-dark',
+ tabSize: 2,
+ automaticLayout: true
+ })
+ })
+ hooks.onUnmounted(() => (state.editor = null))
+ return state
+}
diff --git a/examples/react-docs/index.html b/examples/react-docs/index.html
new file mode 100644
index 000000000..6a8a3c0a3
--- /dev/null
+++ b/examples/react-docs/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Opentiny React 组件调试
+
+
+
+
+
+
diff --git a/examples/react-docs/package.json b/examples/react-docs/package.json
new file mode 100644
index 000000000..478db6c64
--- /dev/null
+++ b/examples/react-docs/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@opentiny/react-docs",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@opentiny/react": "workspace:~",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.14",
+ "@types/react-dom": "^18.2.6",
+ "@typescript-eslint/eslint-plugin": "^5.61.0",
+ "@typescript-eslint/parser": "^5.61.0",
+ "@vitejs/plugin-react": "^4.0.1",
+ "autoprefixer": "^10.4.12",
+ "eslint": "^8.44.0",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "postcss": "^8.4.16",
+ "tailwindcss": "^3.3.3",
+ "typescript": "^5.0.2",
+ "vite": "^4.4.0",
+ "vite-plugin-react": "^4.0.1",
+ "vite-plugin-svgr": "^3.2.0"
+ }
+}
diff --git a/examples/react-docs/postcss.config.mjs b/examples/react-docs/postcss.config.mjs
new file mode 100644
index 000000000..cb9b0aaa5
--- /dev/null
+++ b/examples/react-docs/postcss.config.mjs
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ }
+}
diff --git a/examples/react-docs/public/vite.svg b/examples/react-docs/public/vite.svg
new file mode 100644
index 000000000..e7b8dfb1b
--- /dev/null
+++ b/examples/react-docs/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/react-docs/src/App.tsx b/examples/react-docs/src/App.tsx
new file mode 100644
index 000000000..101e93316
--- /dev/null
+++ b/examples/react-docs/src/App.tsx
@@ -0,0 +1,16 @@
+import { Alert } from '@opentiny/react'
+
+// 在这里导入组件,进行 api 调试
+function App() {
+ return (
+
+ )
+}
+
+export default App
diff --git a/examples/react-docs/src/main.css b/examples/react-docs/src/main.css
new file mode 100644
index 000000000..9be80073a
--- /dev/null
+++ b/examples/react-docs/src/main.css
@@ -0,0 +1,8 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+.app {
+ margin: 10px;
+ width: 500px;
+}
\ No newline at end of file
diff --git a/examples/react-docs/src/main.tsx b/examples/react-docs/src/main.tsx
new file mode 100644
index 000000000..dce122516
--- /dev/null
+++ b/examples/react-docs/src/main.tsx
@@ -0,0 +1,8 @@
+// import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App.tsx'
+import './main.css'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+)
diff --git a/examples/react-docs/tailwind.config.cjs b/examples/react-docs/tailwind.config.cjs
new file mode 100644
index 000000000..b802834cb
--- /dev/null
+++ b/examples/react-docs/tailwind.config.cjs
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [
+ '../../packages/react/src/**/*.{css,less,vue,js,jsx,ts,tsx}',
+ '!../../packages/react/src/**/node_modules',
+ ],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+}
\ No newline at end of file
diff --git a/examples/react-docs/tsconfig.json b/examples/react-docs/tsconfig.json
new file mode 100644
index 000000000..a7fc6fbf2
--- /dev/null
+++ b/examples/react-docs/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/examples/react-docs/tsconfig.node.json b/examples/react-docs/tsconfig.node.json
new file mode 100644
index 000000000..42872c59f
--- /dev/null
+++ b/examples/react-docs/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/examples/react-docs/vite.config.ts b/examples/react-docs/vite.config.ts
new file mode 100644
index 000000000..4ec11151b
--- /dev/null
+++ b/examples/react-docs/vite.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import svgr from "vite-plugin-svgr";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [
+ react({ include: /\.(mdx|js|jsx|ts|tsx)$/ }), svgr()
+ ],
+})
diff --git a/examples/react-site/.prettierrc.js b/examples/react-site/.prettierrc.js
new file mode 100644
index 000000000..1be6b5a02
--- /dev/null
+++ b/examples/react-site/.prettierrc.js
@@ -0,0 +1,15 @@
+module.exports = {
+ printWidth: 160, // 一行120字符数,如果超过会进行换行
+ tabWidth: 2, // tab等2个空格
+ useTabs: false, // 用空格缩进行
+ semi: true, // 行尾使用分号
+ singleQuote: true, // 字符串使用单引号
+ quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
+ jsxSingleQuote: false, // 在JSX中使用双引号
+ trailingComma: 'es5', // 使用尾逗号(对象、数组等)
+ bracketSpacing: true, // 对象的括号间增加空格
+ jsxBracketSameLine: false, // 将多行JSX元素的>放在最后一行的末尾
+ arrowParens: 'avoid', // 在唯一的arrow函数参数周围省略括号
+ vueIndentScriptAndStyle: false, // 不缩进Vue文件中的
diff --git a/examples/react-site/demos/app/alert/center.jsx b/examples/react-site/demos/app/alert/center.jsx
new file mode 100644
index 000000000..47037f9d9
--- /dev/null
+++ b/examples/react-site/demos/app/alert/center.jsx
@@ -0,0 +1,17 @@
+import { Alert as TinyAlert } from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/alert/center.vue b/examples/react-site/demos/app/alert/center.vue
new file mode 100644
index 000000000..138a5be43
--- /dev/null
+++ b/examples/react-site/demos/app/alert/center.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/examples/react-site/demos/app/alert/size.jsx b/examples/react-site/demos/app/alert/size.jsx
new file mode 100644
index 000000000..a14fc76a2
--- /dev/null
+++ b/examples/react-site/demos/app/alert/size.jsx
@@ -0,0 +1,18 @@
+import { Alert as TinyAlert } from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/alert/size.vue b/examples/react-site/demos/app/alert/size.vue
new file mode 100644
index 000000000..39684a6ff
--- /dev/null
+++ b/examples/react-site/demos/app/alert/size.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/examples/react-site/demos/app/alert/title.jsx b/examples/react-site/demos/app/alert/title.jsx
new file mode 100644
index 000000000..61d7149fe
--- /dev/null
+++ b/examples/react-site/demos/app/alert/title.jsx
@@ -0,0 +1,25 @@
+import { Alert as TinyAlert } from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+
+
+ '通过 slot 设置自定义 title'
+ }}
+ >
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/alert/title.vue b/examples/react-site/demos/app/alert/title.vue
new file mode 100644
index 000000000..da9a0c421
--- /dev/null
+++ b/examples/react-site/demos/app/alert/title.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/examples/react-site/demos/app/alert/type.jsx b/examples/react-site/demos/app/alert/type.jsx
new file mode 100644
index 000000000..e2b22d074
--- /dev/null
+++ b/examples/react-site/demos/app/alert/type.jsx
@@ -0,0 +1,21 @@
+import { Alert as TinyAlert } from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+
+
+
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/alert/type.vue b/examples/react-site/demos/app/alert/type.vue
new file mode 100644
index 000000000..f8f288a4b
--- /dev/null
+++ b/examples/react-site/demos/app/alert/type.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/examples/react-site/demos/app/alert/webdoc/alert.cn.md b/examples/react-site/demos/app/alert/webdoc/alert.cn.md
new file mode 100644
index 000000000..819b6c660
--- /dev/null
+++ b/examples/react-site/demos/app/alert/webdoc/alert.cn.md
@@ -0,0 +1,7 @@
+---
+title: Alert 警告
+---
+
+# Alert 警告
+
+Alert 警告,提供 warning、error、info、success 四种类型显示不同类别的信息。
diff --git a/examples/react-site/demos/app/alert/webdoc/alert.en.md b/examples/react-site/demos/app/alert/webdoc/alert.en.md
new file mode 100644
index 000000000..08cd41e21
--- /dev/null
+++ b/examples/react-site/demos/app/alert/webdoc/alert.en.md
@@ -0,0 +1,7 @@
+---
+title: Alert
+---
+
+# Alert
+
+Alert alarms, including warning, error, info, and success.
diff --git a/examples/react-site/demos/app/alert/webdoc/alert.json b/examples/react-site/demos/app/alert/webdoc/alert.json
new file mode 100644
index 000000000..6db2fad42
--- /dev/null
+++ b/examples/react-site/demos/app/alert/webdoc/alert.json
@@ -0,0 +1,228 @@
+{
+ "column": "2",
+ "owner": "",
+ "demos": [
+ {
+ "demoId": "base",
+ "name": {
+ "zh-CN": "基本用法",
+ "en-US": "Basic Usage"
+ },
+ "desc": {
+ "zh-CN": "详细用法参考如下示例",
+ "en-US": "For details, see the following example."
+ },
+ "codeFiles": [
+ "base.vue"
+ ]
+ },
+ {
+ "demoId": "type",
+ "name": {
+ "zh-CN": "类型",
+ "en-US": "Type"
+ },
+ "desc": {
+ "zh-CN": "通过 type
设置不同的类型。可选值:success、warning、info、error,默认值:success 。
\n",
+ "en-US": "Set different types through type
. The options are success, warning, info, and error. The default value is success.
\n"
+ },
+ "codeFiles": [
+ "type.vue"
+ ]
+ },
+ {
+ "demoId": "size",
+ "name": {
+ "zh-CN": "大尺寸",
+ "en-US": "Large Size"
+ },
+ "desc": {
+ "zh-CN": "通过 size
属性设置不同的尺寸,可选值:nomal、large,默认值:nomal 。
\n",
+ "en-US": "Set different sizes through the size
attribute. The options are nomal and large. The default value is nomal.
\n"
+ },
+ "codeFiles": [
+ "size.vue"
+ ]
+ },
+ {
+ "demoId": "title",
+ "name": {
+ "zh-CN": "自定义标题",
+ "en-US": "Custom Title"
+ },
+ "desc": {
+ "zh-CN": "当 size
为 large 时显示标题,可设置 title
或 slot
自定义标题。默认标题根据设置的 type
显示。
\n",
+ "en-US": "When size
is set to large, the title is displayed. You can set title
or slot
to customize the title. The default title is displayed according to the set type
.
\n"
+ },
+ "codeFiles": [
+ "title.vue"
+ ]
+ },
+ {
+ "demoId": "center",
+ "name": {
+ "zh-CN": "文字居中",
+ "en-US": "Center text"
+ },
+ "desc": {
+ "zh-CN": "通过 center
属性可使文字显示居中。
\n",
+ "en-US": "You can use the center
property to center the text.
\n"
+ },
+ "codeFiles": [
+ "center.vue"
+ ]
+ }
+ ],
+ "apis": [
+ {
+ "name": "alert",
+ "type": "component",
+ "properties": [
+ {
+ "name": "closable",
+ "type": "Boolean",
+ "defaultValue": "该属性的默认值为 true",
+ "desc": {
+ "zh-CN": "设置警告是否可以关闭",
+ "en-US": "Set whether alarms can be disabled."
+ },
+ "demoId": "closable"
+ },
+ {
+ "name": "icon",
+ "type": "String , Object",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "设置警告的图标,默认会根据 type 值自动使用对应图标",
+ "en-US": "Set the alarm icon. By default, the corresponding icon is automatically used based on the value of type."
+ },
+ "demoId": "icon"
+ },
+ {
+ "name": "size",
+ "type": "String",
+ "defaultValue": "该属性的默认值为 normal",
+ "desc": {
+ "zh-CN": "设置警告的大小 nomal/large, 缺省为 nomal。;该属性的可选值为 nomal / large",
+ "en-US": "Set the warning size to nomal or large. The default value is nomal. ;The value of this attribute can be nomal or large"
+ },
+ "demoId": "size"
+ },
+ {
+ "name": "title",
+ "type": "String",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "设置警告的标题,在 size 为 large 时有效,默认根据 type 自动设置",
+ "en-US": "Set the warning title. This parameter is valid only when size is set to large. By default, the alarm title is automatically set based on type."
+ },
+ "demoId": "title"
+ },
+ {
+ "name": "type",
+ "type": "String",
+ "defaultValue": "该属性的默认值为 success",
+ "desc": {
+ "zh-CN": "设置警告的类型;该属性的可选值为 success/warning/info/error",
+ "en-US": "Set the alarm type. The value of this attribute can be success / warning / info / error"
+ },
+ "demoId": "type"
+ },
+ {
+ "name": "description",
+ "type": "String",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "设置警告的提示内容,默认为空;",
+ "en-US": "Set the warning prompt content. The default value is null."
+ },
+ "demoId": "custom-description"
+ },
+ {
+ "name": "center",
+ "type": "Boolean",
+ "defaultValue": "该属性的默认值为 false",
+ "desc": {
+ "zh-CN": "文字是否居中",
+ "en-US": "Whether the text is centered"
+ },
+ "demoId": "center"
+ },
+ {
+ "name": "close-text",
+ "type": "String",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "关闭按钮自定义文本",
+ "en-US": "Customized text of the close button"
+ },
+ "demoId": "close-text"
+ },
+ {
+ "name": "show-icon",
+ "type": "Boolean",
+ "defaultValue": "该属性的默认值为 true",
+ "desc": {
+ "zh-CN": "是否显示图标",
+ "en-US": "Display icon"
+ },
+ "demoId": "show-icon"
+ }
+ ],
+ "events": [
+ {
+ "name": "close",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "关闭 alert 时触发的事件",
+ "en-US": "Event triggered when the alert function is disabled"
+ },
+ "demoId": "close-events"
+ }
+ ],
+ "slots": [
+ {
+ "name": "default",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "组件默认插槽",
+ "en-US": "Default slot of the component"
+ },
+ "demoId": "slot-default"
+ },
+ {
+ "name": "title",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "标题的内容",
+ "en-US": "Title content"
+ },
+ "demoId": "title"
+ },
+ {
+ "name": "description",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "提示内容",
+ "en-US": "Prompt Content"
+ },
+ "demoId": "custom-description"
+ },
+ {
+ "name": "close",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "自定义关闭按钮,当 closable 属性为 false 时有效",
+ "en-US": ""
+ },
+ "demoId": "custom-close"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/button/button-click-webcomp.jsx b/examples/react-site/demos/app/button/button-click-webcomp.jsx
new file mode 100644
index 000000000..17c36ec39
--- /dev/null
+++ b/examples/react-site/demos/app/button/button-click-webcomp.jsx
@@ -0,0 +1,17 @@
+import { Button as TinyButton} from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+ 默认按钮
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/button/button-click.vue b/examples/react-site/demos/app/button/button-click.vue
new file mode 100644
index 000000000..96ac564b2
--- /dev/null
+++ b/examples/react-site/demos/app/button/button-click.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/examples/react-site/demos/app/button/button-round-webcomp.jsx b/examples/react-site/demos/app/button/button-round-webcomp.jsx
new file mode 100644
index 000000000..9b26fb019
--- /dev/null
+++ b/examples/react-site/demos/app/button/button-round-webcomp.jsx
@@ -0,0 +1,18 @@
+import { Button as TinyButton} from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+ 主要按钮
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/button/button-round.vue b/examples/react-site/demos/app/button/button-round.vue
new file mode 100644
index 000000000..b747bd02c
--- /dev/null
+++ b/examples/react-site/demos/app/button/button-round.vue
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/examples/react-site/demos/app/button/button-type-webcomp.jsx b/examples/react-site/demos/app/button/button-type-webcomp.jsx
new file mode 100644
index 000000000..d43d7b339
--- /dev/null
+++ b/examples/react-site/demos/app/button/button-type-webcomp.jsx
@@ -0,0 +1,18 @@
+import { Button as TinyButton} from '@pe-3/react'
+import ReactDOM from 'react-dom/client'
+
+function App(props) {
+ return (
+ {props.children}
+
)
+}
+
+export default class extends HTMLElement {
+ connectedCallback() {
+ ReactDOM.createRoot(this).render(
+
+ 成功按钮
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/app/button/button-type.vue b/examples/react-site/demos/app/button/button-type.vue
new file mode 100644
index 000000000..58b20d4be
--- /dev/null
+++ b/examples/react-site/demos/app/button/button-type.vue
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/examples/react-site/demos/app/button/webdoc/button.cn.md b/examples/react-site/demos/app/button/webdoc/button.cn.md
new file mode 100644
index 000000000..62ea794f5
--- /dev/null
+++ b/examples/react-site/demos/app/button/webdoc/button.cn.md
@@ -0,0 +1,16 @@
+---
+title: Button 按钮
+---
+
+# Button 按钮
+
+
+
+按钮组件一般用于触发一些操作。
+
+```typescript
+import { Button } from '@opentiny/vue';
+```
+
+
+
diff --git a/examples/react-site/demos/app/button/webdoc/button.en.md b/examples/react-site/demos/app/button/webdoc/button.en.md
new file mode 100644
index 000000000..8a64f8880
--- /dev/null
+++ b/examples/react-site/demos/app/button/webdoc/button.en.md
@@ -0,0 +1,8 @@
+---
+title: Button 按钮
+---
+
+# Button 按钮
+
+ 按钮组件一般用于触发一些操作。
+
diff --git a/examples/react-site/demos/app/button/webdoc/button.json b/examples/react-site/demos/app/button/webdoc/button.json
new file mode 100644
index 000000000..434c8da1f
--- /dev/null
+++ b/examples/react-site/demos/app/button/webdoc/button.json
@@ -0,0 +1,71 @@
+{
+ "column": "2",
+ "demos": [
+ {
+ "demoId": "button-type",
+ "name": {
+ "zh-CN": "按钮类型",
+ "en-US": "button type"
+ },
+ "desc": {
+ "zh-CN": "通过属性type
配置按钮类型,包含success
、info
、warning
、danger
四种类型。",
+ "en-US": "
button type
"
+ },
+ "codeFiles": ["button-type.vue"]
+ },
+ {
+ "demoId": "button-round",
+ "name": {
+ "zh-CN": "圆角按钮",
+ "en-US": "button round"
+ },
+ "desc": {
+ "zh-CN": "通过round
属性设置按钮是否圆角",
+ "en-US": "
button round
"
+ },
+ "codeFiles": ["button-round.vue"]
+ },
+ {
+ "demoId": "button-click",
+ "name": {
+ "zh-CN": "事件",
+ "en-US": "events"
+ },
+ "desc": {
+ "zh-CN": "按钮点击事件。",
+ "en-US": "
bbutton click
"
+ },
+ "codeFiles": ["button-click.vue"]
+ }
+ ],
+ "apis": [
+ {
+ "name": "Button",
+ "type": "component",
+ "properties": [
+ {
+ "name": "type",
+ "type": "primary | success | warning",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "展示按钮不同的状态
",
+ "en-US": "display different button"
+ },
+ "demoId": "button-type"
+ }
+ ],
+ "events": [
+ {
+ "name": "click",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "点击按钮时触发的回调
",
+ "en-US": "Click"
+ },
+ "demoId": "button-click"
+ }
+ ]
+ }
+ ]
+}
diff --git a/examples/react-site/demos/config.js b/examples/react-site/demos/config.js
new file mode 100644
index 000000000..dabcc1730
--- /dev/null
+++ b/examples/react-site/demos/config.js
@@ -0,0 +1,4 @@
+export default {
+ isMobile: false,
+ initApp: (app) => { }
+}
\ No newline at end of file
diff --git a/examples/react-site/demos/overviewimage/button.svg b/examples/react-site/demos/overviewimage/button.svg
new file mode 100644
index 000000000..7e443c451
--- /dev/null
+++ b/examples/react-site/demos/overviewimage/button.svg
@@ -0,0 +1,106 @@
+
+
+ Button按钮
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/demos/overviewimage/buttongroup.svg b/examples/react-site/demos/overviewimage/buttongroup.svg
new file mode 100644
index 000000000..2dd081880
--- /dev/null
+++ b/examples/react-site/demos/overviewimage/buttongroup.svg
@@ -0,0 +1,138 @@
+
+
+ Buttongroup选块组
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/demos/overviewimage/dev.svg b/examples/react-site/demos/overviewimage/dev.svg
new file mode 100644
index 000000000..fa4b5e668
--- /dev/null
+++ b/examples/react-site/demos/overviewimage/dev.svg
@@ -0,0 +1,76 @@
+
+
+ Modal 弹出框
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 开发中
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/demos/webdoc/introduce.md b/examples/react-site/demos/webdoc/introduce.md
new file mode 100644
index 000000000..4162f22a2
--- /dev/null
+++ b/examples/react-site/demos/webdoc/introduce.md
@@ -0,0 +1,7 @@
+---
+title: 介绍 | TinyReact
+---
+
+# 介绍
+
+TinyReact 简介 md文档
\ No newline at end of file
diff --git a/examples/react-site/demos/webdoc/menus.js b/examples/react-site/demos/webdoc/menus.js
new file mode 100644
index 000000000..483aacb06
--- /dev/null
+++ b/examples/react-site/demos/webdoc/menus.js
@@ -0,0 +1,43 @@
+// 注意,删除了useFor属性
+// title,label增加英文版,以应对将来的国际化功能
+export const standaloneMenus = [
+ {
+ label: '组件总览',
+ key: 'overview',
+ },
+];
+
+export const docMenus = [
+ {
+ label: '使用指南',
+ labelEn: 'Guide', //***********
+ key: 'doc_use',
+ children: [
+ {
+ title: '背景简介',
+ titleEn: 'Introduce',
+ key: 'introduce',
+ },
+ ],
+ },
+];
+
+//-------------------------------------------------------------------
+export const cmpMenus = [
+ {
+ label: '表单选择',
+ labelEn: 'Form Selection',
+ key: 'cmp_formselect',
+ children: [
+ { name: 'Button', nameCn: '按钮', key: 'button' }
+ ]
+ },
+ {
+ 'label': '提示组件',
+ 'labelEn': 'Tips Components',
+ 'key': 'cmp_tips_components',
+ 'children': [
+ { 'nameCn': '警告', 'name': 'Alert', 'key': 'alert' }
+ ]
+ }
+];
diff --git a/examples/react-site/env/.env b/examples/react-site/env/.env
new file mode 100644
index 000000000..6594d266b
--- /dev/null
+++ b/examples/react-site/env/.env
@@ -0,0 +1,2 @@
+# 1、声明一个变量
+VITE_CONTEXT=/tiny-react/
diff --git a/examples/react-site/index.html b/examples/react-site/index.html
new file mode 100644
index 000000000..75a3a9eb0
--- /dev/null
+++ b/examples/react-site/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+ OpenTiny - TinyVue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/md.extend.config.js b/examples/react-site/md.extend.config.js
new file mode 100644
index 000000000..a3b38a513
--- /dev/null
+++ b/examples/react-site/md.extend.config.js
@@ -0,0 +1,41 @@
+import md_prism from 'markdown-it-prism'; // 高亮
+import md_emoji from 'markdown-it-emoji'; // 表情
+import md_sub from 'markdown-it-sub'; // 下标 ~ ~
+import md_sup from 'markdown-it-sup'; // 上标 ^ ^
+import md_mark from 'markdown-it-mark'; // 高亮文字 == ==
+import md_container from 'markdown-it-container'; // 提示块
+import md_anchor from 'markdown-it-anchor';
+export const MdExt = [md_emoji, md_sub, md_sup, md_mark];
+
+// 自定义container
+function createContainer(klass) {
+ return [
+ md_container,
+ klass,
+ {
+ render(tokens, idx) {
+ const token = tokens[idx];
+ const info = token.info.trim().slice(klass.length).trim() || '';
+ if (token.nesting === 1) {
+ return `${info}
\n`;
+ } else {
+ return `
\n`;
+ }
+ },
+ },
+ ];
+}
+
+export function mdInstall(md) {
+ md.use(md_prism, { plugins: ['line-highlight'] })
+ .use(...createContainer('tip'))
+ .use(...createContainer('info'))
+ .use(...createContainer('warning'))
+ .use(...createContainer('danger'))
+ .use(md_anchor, {
+ permalink: true,
+ permalinkBefore: true,
+ permalinkSymbol: '',
+ slugify: s => encodeURIComponent(s),
+ });
+}
diff --git a/examples/react-site/package.json b/examples/react-site/package.json
new file mode 100644
index 000000000..158adbca1
--- /dev/null
+++ b/examples/react-site/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "@opentiny/react-site",
+ "private": true,
+ "version": "0.1.0",
+ "scripts": {
+ "start": "node ./scripts/copy.js && node ./scripts/build-react.mjs && vite",
+ "build:react": "node ./scripts/build-react.mjs",
+ "build": "node ./scripts/copy.js && node ./scripts/build-react.mjs && vite build",
+ "prettier": "npx prettier --write ./**/*.{ts,tsx,css,less,scss}",
+ "stylelint": "npx stylelint ./src/**/*.scss ./src/**/*.less ./src/**/*.css --fix"
+ },
+ "dependencies": {
+ "@babel/preset-env": "^7.22.20",
+ "@opentiny/vue": "^3.10.1",
+ "@pe-3/react": "^1.0.15",
+ "@rollup/plugin-babel": "^6.0.3",
+ "@unocss/reset": "0.38.2",
+ "@vitejs/plugin-react": "^4.0.4",
+ "@vueuse/head": "0.7.13",
+ "dompurify": "^3.0.1",
+ "github-markdown-css": "^5.1.0",
+ "highlight.js": "^11.5.1",
+ "marked": "^4.3.0",
+ "prismjs": "^1.28.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "vite4": "npm:vite@4",
+ "vue": "^3.2.37",
+ "vue-i18n": "^9.1.10",
+ "vue-router": "4.1.5"
+ },
+ "devDependencies": {
+ "@types/markdown-it": "^12.2.3",
+ "@types/node": "^17.0.45",
+ "@unocss/preset-icons": "^0.38.2",
+ "@vitejs/plugin-vue": "^2.3.3",
+ "@vitejs/plugin-vue-jsx": "^1.3.10",
+ "@vue/compiler-sfc": "^3.2.37",
+ "chalk": "4.1.2",
+ "cross-spawn": "^7.0.3",
+ "fast-glob": "^3.2.12",
+ "fs-extra": "^10.1.0",
+ "less": "^4.1.3",
+ "markdown-it": "^13.0.1",
+ "markdown-it-anchor": "^8.6.4",
+ "markdown-it-container": "^3.0.0",
+ "markdown-it-emoji": "^2.0.2",
+ "markdown-it-mark": "^3.0.1",
+ "markdown-it-prism": "^2.2.4",
+ "markdown-it-sub": "^1.0.0",
+ "markdown-it-sup": "^1.0.0",
+ "markdown-it-table-of-contents": "^0.6.0",
+ "markdown-it-toc-done-right": "^4.2.0",
+ "naive-ui": "^2.30.6",
+ "prettier": "^2.7.1",
+ "stylelint": "^14.9.1",
+ "stylelint-config-standard": "^26.0.0",
+ "unocss": "^0.39.3",
+ "unplugin-auto-import": "0.8.7",
+ "unplugin-vue-components": "^0.19.9",
+ "uslug": "^1.0.4",
+ "vite": "^2.9.12",
+ "vite-plugin-html": "^2.0.0",
+ "vite-plugin-inspect": "^0.5.0",
+ "vite-plugin-md": "0.13.1"
+ }
+}
diff --git a/examples/react-site/public/@demos/app/alert/base.vue b/examples/react-site/public/@demos/app/alert/base.vue
new file mode 100644
index 000000000..cdac59438
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/base.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/alert/center.vue b/examples/react-site/public/@demos/app/alert/center.vue
new file mode 100644
index 000000000..138a5be43
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/center.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/alert/size.vue b/examples/react-site/public/@demos/app/alert/size.vue
new file mode 100644
index 000000000..39684a6ff
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/size.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/alert/title.vue b/examples/react-site/public/@demos/app/alert/title.vue
new file mode 100644
index 000000000..da9a0c421
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/title.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/alert/type.vue b/examples/react-site/public/@demos/app/alert/type.vue
new file mode 100644
index 000000000..f8f288a4b
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/type.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/alert/webdoc/alert.cn.md b/examples/react-site/public/@demos/app/alert/webdoc/alert.cn.md
new file mode 100644
index 000000000..819b6c660
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/webdoc/alert.cn.md
@@ -0,0 +1,7 @@
+---
+title: Alert 警告
+---
+
+# Alert 警告
+
+Alert 警告,提供 warning、error、info、success 四种类型显示不同类别的信息。
diff --git a/examples/react-site/public/@demos/app/alert/webdoc/alert.en.md b/examples/react-site/public/@demos/app/alert/webdoc/alert.en.md
new file mode 100644
index 000000000..08cd41e21
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/webdoc/alert.en.md
@@ -0,0 +1,7 @@
+---
+title: Alert
+---
+
+# Alert
+
+Alert alarms, including warning, error, info, and success.
diff --git a/examples/react-site/public/@demos/app/alert/webdoc/alert.json b/examples/react-site/public/@demos/app/alert/webdoc/alert.json
new file mode 100644
index 000000000..6db2fad42
--- /dev/null
+++ b/examples/react-site/public/@demos/app/alert/webdoc/alert.json
@@ -0,0 +1,228 @@
+{
+ "column": "2",
+ "owner": "",
+ "demos": [
+ {
+ "demoId": "base",
+ "name": {
+ "zh-CN": "基本用法",
+ "en-US": "Basic Usage"
+ },
+ "desc": {
+ "zh-CN": "详细用法参考如下示例",
+ "en-US": "For details, see the following example."
+ },
+ "codeFiles": [
+ "base.vue"
+ ]
+ },
+ {
+ "demoId": "type",
+ "name": {
+ "zh-CN": "类型",
+ "en-US": "Type"
+ },
+ "desc": {
+ "zh-CN": "通过 type
设置不同的类型。可选值:success、warning、info、error,默认值:success 。
\n",
+ "en-US": "Set different types through type
. The options are success, warning, info, and error. The default value is success.
\n"
+ },
+ "codeFiles": [
+ "type.vue"
+ ]
+ },
+ {
+ "demoId": "size",
+ "name": {
+ "zh-CN": "大尺寸",
+ "en-US": "Large Size"
+ },
+ "desc": {
+ "zh-CN": "通过 size
属性设置不同的尺寸,可选值:nomal、large,默认值:nomal 。
\n",
+ "en-US": "Set different sizes through the size
attribute. The options are nomal and large. The default value is nomal.
\n"
+ },
+ "codeFiles": [
+ "size.vue"
+ ]
+ },
+ {
+ "demoId": "title",
+ "name": {
+ "zh-CN": "自定义标题",
+ "en-US": "Custom Title"
+ },
+ "desc": {
+ "zh-CN": "当 size
为 large 时显示标题,可设置 title
或 slot
自定义标题。默认标题根据设置的 type
显示。
\n",
+ "en-US": "When size
is set to large, the title is displayed. You can set title
or slot
to customize the title. The default title is displayed according to the set type
.
\n"
+ },
+ "codeFiles": [
+ "title.vue"
+ ]
+ },
+ {
+ "demoId": "center",
+ "name": {
+ "zh-CN": "文字居中",
+ "en-US": "Center text"
+ },
+ "desc": {
+ "zh-CN": "通过 center
属性可使文字显示居中。
\n",
+ "en-US": "You can use the center
property to center the text.
\n"
+ },
+ "codeFiles": [
+ "center.vue"
+ ]
+ }
+ ],
+ "apis": [
+ {
+ "name": "alert",
+ "type": "component",
+ "properties": [
+ {
+ "name": "closable",
+ "type": "Boolean",
+ "defaultValue": "该属性的默认值为 true",
+ "desc": {
+ "zh-CN": "设置警告是否可以关闭",
+ "en-US": "Set whether alarms can be disabled."
+ },
+ "demoId": "closable"
+ },
+ {
+ "name": "icon",
+ "type": "String , Object",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "设置警告的图标,默认会根据 type 值自动使用对应图标",
+ "en-US": "Set the alarm icon. By default, the corresponding icon is automatically used based on the value of type."
+ },
+ "demoId": "icon"
+ },
+ {
+ "name": "size",
+ "type": "String",
+ "defaultValue": "该属性的默认值为 normal",
+ "desc": {
+ "zh-CN": "设置警告的大小 nomal/large, 缺省为 nomal。;该属性的可选值为 nomal / large",
+ "en-US": "Set the warning size to nomal or large. The default value is nomal. ;The value of this attribute can be nomal or large"
+ },
+ "demoId": "size"
+ },
+ {
+ "name": "title",
+ "type": "String",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "设置警告的标题,在 size 为 large 时有效,默认根据 type 自动设置",
+ "en-US": "Set the warning title. This parameter is valid only when size is set to large. By default, the alarm title is automatically set based on type."
+ },
+ "demoId": "title"
+ },
+ {
+ "name": "type",
+ "type": "String",
+ "defaultValue": "该属性的默认值为 success",
+ "desc": {
+ "zh-CN": "设置警告的类型;该属性的可选值为 success/warning/info/error",
+ "en-US": "Set the alarm type. The value of this attribute can be success / warning / info / error"
+ },
+ "demoId": "type"
+ },
+ {
+ "name": "description",
+ "type": "String",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "设置警告的提示内容,默认为空;",
+ "en-US": "Set the warning prompt content. The default value is null."
+ },
+ "demoId": "custom-description"
+ },
+ {
+ "name": "center",
+ "type": "Boolean",
+ "defaultValue": "该属性的默认值为 false",
+ "desc": {
+ "zh-CN": "文字是否居中",
+ "en-US": "Whether the text is centered"
+ },
+ "demoId": "center"
+ },
+ {
+ "name": "close-text",
+ "type": "String",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "关闭按钮自定义文本",
+ "en-US": "Customized text of the close button"
+ },
+ "demoId": "close-text"
+ },
+ {
+ "name": "show-icon",
+ "type": "Boolean",
+ "defaultValue": "该属性的默认值为 true",
+ "desc": {
+ "zh-CN": "是否显示图标",
+ "en-US": "Display icon"
+ },
+ "demoId": "show-icon"
+ }
+ ],
+ "events": [
+ {
+ "name": "close",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "关闭 alert 时触发的事件",
+ "en-US": "Event triggered when the alert function is disabled"
+ },
+ "demoId": "close-events"
+ }
+ ],
+ "slots": [
+ {
+ "name": "default",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "组件默认插槽",
+ "en-US": "Default slot of the component"
+ },
+ "demoId": "slot-default"
+ },
+ {
+ "name": "title",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "标题的内容",
+ "en-US": "Title content"
+ },
+ "demoId": "title"
+ },
+ {
+ "name": "description",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "提示内容",
+ "en-US": "Prompt Content"
+ },
+ "demoId": "custom-description"
+ },
+ {
+ "name": "close",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "自定义关闭按钮,当 closable 属性为 false 时有效",
+ "en-US": ""
+ },
+ "demoId": "custom-close"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/react-site/public/@demos/app/button/button-click.vue b/examples/react-site/public/@demos/app/button/button-click.vue
new file mode 100644
index 000000000..96ac564b2
--- /dev/null
+++ b/examples/react-site/public/@demos/app/button/button-click.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/button/button-round.vue b/examples/react-site/public/@demos/app/button/button-round.vue
new file mode 100644
index 000000000..b747bd02c
--- /dev/null
+++ b/examples/react-site/public/@demos/app/button/button-round.vue
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/button/button-type.vue b/examples/react-site/public/@demos/app/button/button-type.vue
new file mode 100644
index 000000000..58b20d4be
--- /dev/null
+++ b/examples/react-site/public/@demos/app/button/button-type.vue
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/examples/react-site/public/@demos/app/button/webdoc/button.cn.md b/examples/react-site/public/@demos/app/button/webdoc/button.cn.md
new file mode 100644
index 000000000..62ea794f5
--- /dev/null
+++ b/examples/react-site/public/@demos/app/button/webdoc/button.cn.md
@@ -0,0 +1,16 @@
+---
+title: Button 按钮
+---
+
+# Button 按钮
+
+
+
+按钮组件一般用于触发一些操作。
+
+```typescript
+import { Button } from '@opentiny/vue';
+```
+
+
+
diff --git a/examples/react-site/public/@demos/app/button/webdoc/button.en.md b/examples/react-site/public/@demos/app/button/webdoc/button.en.md
new file mode 100644
index 000000000..8a64f8880
--- /dev/null
+++ b/examples/react-site/public/@demos/app/button/webdoc/button.en.md
@@ -0,0 +1,8 @@
+---
+title: Button 按钮
+---
+
+# Button 按钮
+
+ 按钮组件一般用于触发一些操作。
+
diff --git a/examples/react-site/public/@demos/app/button/webdoc/button.json b/examples/react-site/public/@demos/app/button/webdoc/button.json
new file mode 100644
index 000000000..434c8da1f
--- /dev/null
+++ b/examples/react-site/public/@demos/app/button/webdoc/button.json
@@ -0,0 +1,71 @@
+{
+ "column": "2",
+ "demos": [
+ {
+ "demoId": "button-type",
+ "name": {
+ "zh-CN": "按钮类型",
+ "en-US": "button type"
+ },
+ "desc": {
+ "zh-CN": "通过属性type
配置按钮类型,包含success
、info
、warning
、danger
四种类型。",
+ "en-US": "
button type
"
+ },
+ "codeFiles": ["button-type.vue"]
+ },
+ {
+ "demoId": "button-round",
+ "name": {
+ "zh-CN": "圆角按钮",
+ "en-US": "button round"
+ },
+ "desc": {
+ "zh-CN": "通过round
属性设置按钮是否圆角",
+ "en-US": "
button round
"
+ },
+ "codeFiles": ["button-round.vue"]
+ },
+ {
+ "demoId": "button-click",
+ "name": {
+ "zh-CN": "事件",
+ "en-US": "events"
+ },
+ "desc": {
+ "zh-CN": "按钮点击事件。",
+ "en-US": "
bbutton click
"
+ },
+ "codeFiles": ["button-click.vue"]
+ }
+ ],
+ "apis": [
+ {
+ "name": "Button",
+ "type": "component",
+ "properties": [
+ {
+ "name": "type",
+ "type": "primary | success | warning",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "展示按钮不同的状态
",
+ "en-US": "display different button"
+ },
+ "demoId": "button-type"
+ }
+ ],
+ "events": [
+ {
+ "name": "click",
+ "type": "",
+ "defaultValue": "",
+ "desc": {
+ "zh-CN": "点击按钮时触发的回调
",
+ "en-US": "Click"
+ },
+ "demoId": "button-click"
+ }
+ ]
+ }
+ ]
+}
diff --git a/examples/react-site/public/@demos/config.js b/examples/react-site/public/@demos/config.js
new file mode 100644
index 000000000..dabcc1730
--- /dev/null
+++ b/examples/react-site/public/@demos/config.js
@@ -0,0 +1,4 @@
+export default {
+ isMobile: false,
+ initApp: (app) => { }
+}
\ No newline at end of file
diff --git a/examples/react-site/public/@demos/overviewimage/button.svg b/examples/react-site/public/@demos/overviewimage/button.svg
new file mode 100644
index 000000000..7e443c451
--- /dev/null
+++ b/examples/react-site/public/@demos/overviewimage/button.svg
@@ -0,0 +1,106 @@
+
+
+ Button按钮
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/public/@demos/overviewimage/buttongroup.svg b/examples/react-site/public/@demos/overviewimage/buttongroup.svg
new file mode 100644
index 000000000..2dd081880
--- /dev/null
+++ b/examples/react-site/public/@demos/overviewimage/buttongroup.svg
@@ -0,0 +1,138 @@
+
+
+ Buttongroup选块组
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/public/@demos/overviewimage/dev.svg b/examples/react-site/public/@demos/overviewimage/dev.svg
new file mode 100644
index 000000000..fa4b5e668
--- /dev/null
+++ b/examples/react-site/public/@demos/overviewimage/dev.svg
@@ -0,0 +1,76 @@
+
+
+ Modal 弹出框
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 开发中
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/public/@demos/webdoc/introduce.md b/examples/react-site/public/@demos/webdoc/introduce.md
new file mode 100644
index 000000000..4162f22a2
--- /dev/null
+++ b/examples/react-site/public/@demos/webdoc/introduce.md
@@ -0,0 +1,7 @@
+---
+title: 介绍 | TinyReact
+---
+
+# 介绍
+
+TinyReact 简介 md文档
\ No newline at end of file
diff --git a/examples/react-site/public/@demos/webdoc/menus.js b/examples/react-site/public/@demos/webdoc/menus.js
new file mode 100644
index 000000000..483aacb06
--- /dev/null
+++ b/examples/react-site/public/@demos/webdoc/menus.js
@@ -0,0 +1,43 @@
+// 注意,删除了useFor属性
+// title,label增加英文版,以应对将来的国际化功能
+export const standaloneMenus = [
+ {
+ label: '组件总览',
+ key: 'overview',
+ },
+];
+
+export const docMenus = [
+ {
+ label: '使用指南',
+ labelEn: 'Guide', //***********
+ key: 'doc_use',
+ children: [
+ {
+ title: '背景简介',
+ titleEn: 'Introduce',
+ key: 'introduce',
+ },
+ ],
+ },
+];
+
+//-------------------------------------------------------------------
+export const cmpMenus = [
+ {
+ label: '表单选择',
+ labelEn: 'Form Selection',
+ key: 'cmp_formselect',
+ children: [
+ { name: 'Button', nameCn: '按钮', key: 'button' }
+ ]
+ },
+ {
+ 'label': '提示组件',
+ 'labelEn': 'Tips Components',
+ 'key': 'cmp_tips_components',
+ 'children': [
+ { 'nameCn': '警告', 'name': 'Alert', 'key': 'alert' }
+ ]
+ }
+];
diff --git a/examples/react-site/public/favicon.ico b/examples/react-site/public/favicon.ico
new file mode 100644
index 000000000..826d98acb
Binary files /dev/null and b/examples/react-site/public/favicon.ico differ
diff --git a/examples/react-site/scripts/build-react.mjs b/examples/react-site/scripts/build-react.mjs
new file mode 100755
index 000000000..a9ccdef6f
--- /dev/null
+++ b/examples/react-site/scripts/build-react.mjs
@@ -0,0 +1,75 @@
+import { build, defineConfig } from 'vite4'
+import fg from 'fast-glob'
+import { getBabelOutputPlugin } from '@rollup/plugin-babel'
+
+function createEntry() {
+ const entries = fg.sync(
+ ['demos/**/*.jsx']
+ )
+ return entries.reduce((pre, item) => {
+ pre[
+ item
+ .replace('demos/app/', '')
+ .replace('.jsx', '')
+ ] = item
+ return pre
+ }, ({}))
+}
+
+const entries = createEntry()
+
+function prependPlugin(options) {
+ return {
+ name: 'prepend-plugin',
+ generateBundle(_, bundle) {
+ for (const fileName in bundle) {
+ const chunk = bundle[fileName];
+ if (chunk.isEntry) {
+ chunk.code = `${options.code}\n${chunk.code}`;
+ }
+ }
+ },
+ };
+}
+
+async function buildReact() {
+ await build({
+ ...defineConfig({
+ publicDir: false,
+ extensions: ['.js', '.ts', '.tsx', '.jsx'],
+ plugins: []
+ }),
+ configFile: false,
+ build: {
+ outDir: './src/webcomps',
+ emptyOutDir: true,
+ minify: false,
+ rollupOptions: {
+ plugins: [
+ getBabelOutputPlugin({
+ presets: [['@babel/preset-env', { loose: true, modules: false }]]
+ }),
+ prependPlugin({
+ code: `import React from 'react'`
+ })
+ ],
+ output: {
+ strict: false,
+ manualChunks: {}
+ },
+ external: [
+ /^@pe-3/,
+ /^@opentiny/,
+ /^react/
+ ]
+ },
+ lib: {
+ entry: entries,
+ formats: ['es'],
+ fileName: (format, entryName) => `${entryName}.js`
+ }
+ }
+ })
+}
+
+buildReact()
\ No newline at end of file
diff --git a/examples/react-site/scripts/copy.js b/examples/react-site/scripts/copy.js
new file mode 100644
index 000000000..c56f760c9
--- /dev/null
+++ b/examples/react-site/scripts/copy.js
@@ -0,0 +1,32 @@
+const fs = require('fs-extra');
+const path = require('path');
+const chalk = require('chalk');
+
+const baseDir = process.cwd();
+const toRemovefiles = [path.resolve(baseDir, './public/@demos')];
+
+toRemovefiles.forEach(file => {
+ if (fs.pathExistsSync(file)) {
+ fs.removeSync(file);
+ }
+});
+
+const copyfiles = [
+ {
+ // 组件示例源码、组件描述markdown和组件示例配置
+ source: 'demos',
+ target: './public/@demos',
+ },
+];
+
+// 根据传入的参数,同步拷贝相应的文件
+copyfiles.forEach(path => {
+ try {
+ fs.copySync(path.source, path.target, (src) => {
+ return !/.jsx$/.test(src);
+ });
+ console.log(chalk.green(path.source + ' 拷贝完成!'));
+ } catch (err) {
+ console.log(chalk.red(err));
+ }
+});
diff --git a/examples/react-site/src/App.vue b/examples/react-site/src/App.vue
new file mode 100644
index 000000000..4972a7575
--- /dev/null
+++ b/examples/react-site/src/App.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
diff --git a/examples/react-site/src/assets/custom-block.less b/examples/react-site/src/assets/custom-block.less
new file mode 100644
index 000000000..a47355709
--- /dev/null
+++ b/examples/react-site/src/assets/custom-block.less
@@ -0,0 +1,144 @@
+.markdown-body ul,
+.markdown-body ol,
+.markdown-body li {
+ list-style: circle;
+}
+// 切换md中,这2个类的显示隐藏. used-config used-tiny 类
+.md-config .markdown-body {
+ .used-tiny {
+ display: none;
+ }
+}
+.md-tiny .markdown-body {
+ .used-config {
+ display: none;
+ }
+}
+// 表格组件demo区会将其父容器(.n-layout-scroll-container)宽度撑开超出中间内容区,设置此样式限制demo区容器的宽度
+.md-tiny .n-layout-scroll-container {
+ width: 100%;
+ scroll-behavior: smooth;
+}
+.n-code pre {
+ line-height: 1.5;
+}
+.markdown-body {
+ font-size: 15px;
+ h1, h2, h3 {
+ border-bottom: none;
+ }
+ p {
+ font-size: 14px;
+ }
+ code {
+ margin: 0 4px;
+ padding: 0.2em 0.4em;
+ background: rgba(0, 0, 0, 0.04);
+ border: 1px solid rgba(5, 5, 5, 0.06);
+ border-radius: 3px;
+ }
+ pre {
+ background-color: rgba(0, 0, 0, 0.04);
+ }
+ a {
+ color: #5073e5;
+ &:hover {
+ color: #69b1ff;
+ text-decoration: none;
+ }
+ }
+}
+
+// 以下是提示块的样式
+.theme-light {
+ .markdown-body {
+ --color-tip-bg: #f3f5f7;
+ --color-tip-fg: #24292f;
+ --color-tip-bd: #3eaf7c;
+ --color-tip-title: #24292f;
+
+ --color-info-bg: #f3f5f7;
+ --color-info-fg: #24292f;
+ --color-info-bd: #476582;
+ --color-info-title: #24292f;
+
+ --color-warning-bg: #ffe5644d;
+ --color-warning-fg: #6b5900;
+ --color-warning-bd: #e7c000;
+ --color-warning-title: #b29400;
+
+ --color-danger-bg: #ffe6e6;
+ --color-danger-fg: #4d0000;
+ --color-danger-bd: #c00;
+ --color-danger-title: #900;
+ }
+}
+.theme-dark {
+ .markdown-body {
+ --color-tip-fg: #d3d5d6;
+ --color-tip-bg: #24292f;
+ --color-tip-bd: #3eaf7c;
+ --color-tip-title: #f3f5f7;
+
+ --color-info-fg: #d3d5d6;
+ --color-info-bg: #24292f;
+ --color-info-bd: #476582;
+ --color-info-title: #f3f5f7;
+
+ --color-warning-fg: #ffe564;
+ --color-warning-bg: #6b59004d;
+ --color-warning-bd: #e7c000;
+ --color-warning-title: #b89e1d;
+
+ --color-danger-fg: #f17070;
+ --color-danger-bg: #6b0b0b;
+ --color-danger-bd: #c00;
+ --color-danger-title: #df8686;
+ }
+}
+.markdown-body .custom-block.tip,
+.custom-block.info,
+.custom-block.warning,
+.custom-block.danger {
+ margin: 1rem 0;
+ border-left: 0.5rem solid;
+ padding: 0.1rem 1.5rem;
+ overflow-x: auto;
+}
+.markdown-body {
+ .custom-block.tip {
+ background-color: var(--color-tip-bg);
+ color: var(--color-tip-fg);
+ border-color: var(--color-tip-bd);
+ .custom-block-title {
+ color: var(--color-tip-title);
+ }
+ }
+
+ .custom-block.info {
+ background-color: var(--color-info-bg);
+ color: var(--color-info-fg);
+ border-color: var(--color-info-bd);
+ .custom-block-title {
+ color: var(--color-info-title);
+ }
+ }
+
+ .custom-block.warning {
+ background-color: var(--color-warning-bg);
+ color: var(--color-warning-fg);
+ border-color: var(--color-warning-bd);
+ .custom-block-title {
+ color: var(--color-warning-title);
+ }
+ }
+
+ .custom-block.danger {
+ background-color: var(--color-danger-bg);
+ color: var(--color-danger-fg);
+ border-color: var(--color-danger-bd);
+ .custom-block-title {
+ color: var(--color-danger-title);
+ }
+ }
+}
diff --git a/examples/react-site/src/assets/custom-markdown.css b/examples/react-site/src/assets/custom-markdown.css
new file mode 100644
index 000000000..abca6acc1
--- /dev/null
+++ b/examples/react-site/src/assets/custom-markdown.css
@@ -0,0 +1,94 @@
+.markdown-body hr,
+.markdown-body hr + h2 {
+ display: none;
+}
+.theme-dark .markdown-body {
+ color-scheme: dark;
+ --color-prettylights-syntax-comment: #8b949e;
+ --color-prettylights-syntax-constant: #79c0ff;
+ --color-prettylights-syntax-entity: #d2a8ff;
+ --color-prettylights-syntax-storage-modifier-import: #c9d1d9;
+ --color-prettylights-syntax-entity-tag: #7ee787;
+ --color-prettylights-syntax-keyword: #ff7b72;
+ --color-prettylights-syntax-string: #a5d6ff;
+ --color-prettylights-syntax-variable: #ffa657;
+ --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
+ --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
+ --color-prettylights-syntax-invalid-illegal-bg: #8e1519;
+ --color-prettylights-syntax-carriage-return-text: #f0f6fc;
+ --color-prettylights-syntax-carriage-return-bg: #b62324;
+ --color-prettylights-syntax-string-regexp: #7ee787;
+ --color-prettylights-syntax-markup-list: #f2cc60;
+ --color-prettylights-syntax-markup-heading: #1f6feb;
+ --color-prettylights-syntax-markup-italic: #c9d1d9;
+ --color-prettylights-syntax-markup-bold: #c9d1d9;
+ --color-prettylights-syntax-markup-deleted-text: #ffdcd7;
+ --color-prettylights-syntax-markup-deleted-bg: #67060c;
+ --color-prettylights-syntax-markup-inserted-text: #aff5b4;
+ --color-prettylights-syntax-markup-inserted-bg: #033a16;
+ --color-prettylights-syntax-markup-changed-text: #ffdfb6;
+ --color-prettylights-syntax-markup-changed-bg: #5a1e02;
+ --color-prettylights-syntax-markup-ignored-text: #c9d1d9;
+ --color-prettylights-syntax-markup-ignored-bg: #1158c7;
+ --color-prettylights-syntax-meta-diff-range: #d2a8ff;
+ --color-prettylights-syntax-brackethighlighter-angle: #8b949e;
+ --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
+ --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
+ --color-fg-default: #c9d1d9;
+ --color-fg-muted: #8b949e;
+ --color-fg-subtle: #484f58;
+ --color-canvas-default: #0d1117;
+ --color-canvas-subtle: #161b22;
+ --color-border-default: #30363d;
+ --color-border-muted: #21262d;
+ --color-neutral-muted: rgba(110, 118, 129, 0.4);
+ --color-accent-fg: #58a6ff;
+ --color-accent-emphasis: #1f6feb;
+ --color-attention-subtle: rgba(187, 128, 9, 0.15);
+ --color-danger-fg: #f85149;
+}
+.theme-light .markdown-body {
+ color-scheme: light;
+ --color-prettylights-syntax-comment: #6e7781;
+ --color-prettylights-syntax-constant: #0550ae;
+ --color-prettylights-syntax-entity: #8250df;
+ --color-prettylights-syntax-storage-modifier-import: #24292f;
+ --color-prettylights-syntax-entity-tag: #116329;
+ --color-prettylights-syntax-keyword: #cf222e;
+ --color-prettylights-syntax-string: #0a3069;
+ --color-prettylights-syntax-variable: #953800;
+ --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
+ --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
+ --color-prettylights-syntax-invalid-illegal-bg: #82071e;
+ --color-prettylights-syntax-carriage-return-text: #f6f8fa;
+ --color-prettylights-syntax-carriage-return-bg: #cf222e;
+ --color-prettylights-syntax-string-regexp: #116329;
+ --color-prettylights-syntax-markup-list: #3b2300;
+ --color-prettylights-syntax-markup-heading: #0550ae;
+ --color-prettylights-syntax-markup-italic: #24292f;
+ --color-prettylights-syntax-markup-bold: #24292f;
+ --color-prettylights-syntax-markup-deleted-text: #82071e;
+ --color-prettylights-syntax-markup-deleted-bg: #ffebe9;
+ --color-prettylights-syntax-markup-inserted-text: #116329;
+ --color-prettylights-syntax-markup-inserted-bg: #dafbe1;
+ --color-prettylights-syntax-markup-changed-text: #953800;
+ --color-prettylights-syntax-markup-changed-bg: #ffd8b5;
+ --color-prettylights-syntax-markup-ignored-text: #eaeef2;
+ --color-prettylights-syntax-markup-ignored-bg: #0550ae;
+ --color-prettylights-syntax-meta-diff-range: #8250df;
+ --color-prettylights-syntax-brackethighlighter-angle: #57606a;
+ --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
+ --color-prettylights-syntax-constant-other-reference-link: #0a3069;
+ --color-fg-default: #24292f;
+ --color-fg-muted: #57606a;
+ --color-fg-subtle: #6e7781;
+ --color-canvas-default: #ffffff;
+ --color-canvas-subtle: #f6f8fa;
+ --color-border-default: #d0d7de;
+ --color-border-muted: hsla(210, 18%, 87%, 1);
+ --color-neutral-muted: rgba(175, 184, 193, 0.2);
+ --color-accent-fg: #0969da;
+ --color-accent-emphasis: #0969da;
+ --color-attention-subtle: #fff8c5;
+ --color-danger-fg: #cf222e;
+}
diff --git a/examples/react-site/src/assets/images/no-data.svg b/examples/react-site/src/assets/images/no-data.svg
new file mode 100644
index 000000000..7f9ddfb71
--- /dev/null
+++ b/examples/react-site/src/assets/images/no-data.svg
@@ -0,0 +1,141 @@
+
+
+
+ 插图/无搜索结果/h150px
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/src/assets/images/search.svg b/examples/react-site/src/assets/images/search.svg
new file mode 100644
index 000000000..ce5d77f61
--- /dev/null
+++ b/examples/react-site/src/assets/images/search.svg
@@ -0,0 +1,16 @@
+
+
+
\ No newline at end of file
diff --git a/examples/react-site/src/assets/index.less b/examples/react-site/src/assets/index.less
new file mode 100644
index 000000000..a42bf25c5
--- /dev/null
+++ b/examples/react-site/src/assets/index.less
@@ -0,0 +1,67 @@
+// 重置代码
+html,
+body {
+ height: 100%;
+ width: 100%;
+ scroll-behavior: smooth;
+ font-size: 12px;
+}
+* {
+ box-sizing: border-box;
+}
+:root {
+ --white: #fff;
+ --black: #000;
+ --main: #2b292d; // 主字体色
+ --mainless: #2c2e30;
+ --second: #7e8085; // 次级字色
+ --secondless: lighten(#7e8085, 20%); // 次级字色
+
+ --light: #f6f8f9; // 非常浅的背景色
+ --lightless: #f1f2f3; // 非常浅的
+
+ --primary: #2f5bea; // 四个主色
+ --success: #5073e5;
+ --warning: #ffbe0e;
+ --error: #ee343f;
+ --primaryless: lighten(#2f5bea, 20%); // 四个主色的浅色,比如鼠标移上时
+ --successless: lighten(#5073e5, 20%);
+ --warningless: lighten(#ffbe0e, 20%);
+ --errorless: lighten(#ee343f, 20%);
+
+ --border-style: solid;
+ --border-color: #ddd;
+ --border-width: 1px;
+ --radius-sm: 5px;
+ --radius-lg: 30px;
+ --shadow-sm: 0px 1px 3px 3px RGBA(0, 0, 0, 0.1);
+ --shadow-lg: 0px 6px 18px 18px RGBA(0, 0, 0, 0.1);
+
+ --text-shadow-sm: rgb(0, 0, 0, 0.4) 0px 1px 3px;
+ --text-shadow-lg: rgb(0, 0, 0, 0.4) 0px 6px 3px;
+
+ --mask: fade(#000, 45%);
+ --trans-time: 0.4s;
+ --trans-time-slow: 1s;
+
+ // echarts 的9种颜色
+ --c0: #efefef;
+ --c1: #5470c6;
+ --c2: #91cc75;
+ --c3: #fac858;
+ --c4: #ee6666;
+ --c5: #73c0de;
+ --c6: #3ba272;
+ --c7: #fc8452;
+ --c8: #9a60b4;
+ --c9: #ea7ccc;
+}
+
+.theme-dark {
+ --lightless: #3d3d3d; // 非常浅的
+ --border-color: #6b6a6a;
+}
+
+[draggable='true'] {
+ user-select: none;
+}
diff --git a/examples/react-site/src/i18n/en.json b/examples/react-site/src/i18n/en.json
new file mode 100644
index 000000000..27970fff9
--- /dev/null
+++ b/examples/react-site/src/i18n/en.json
@@ -0,0 +1,23 @@
+{
+ "dark": "Dark",
+ "light": "Light",
+ "searchPlaceholder": "Search",
+ "home": "Home",
+ "doc": "Docs",
+ "component": "Components",
+ "common": "Common",
+ "apiPreference": "Framework",
+ "apiTiny": "Vue",
+ "yan-shi": "Demo",
+ "name": "Name",
+ "propType": "Type",
+ "defValue": "Default",
+ "typeValue": "Option Value",
+ "desc": "Description",
+ "showCode": "Show Code",
+ "hideCode": "Hide Code",
+ "copyCode": "Copy Code",
+ "doc-owner": "Owner",
+ "copyCodeOk": "Copy Success",
+ "frameAngular": "Angular"
+}
diff --git a/examples/react-site/src/i18n/index.js b/examples/react-site/src/i18n/index.js
new file mode 100644
index 000000000..2d26be4c9
--- /dev/null
+++ b/examples/react-site/src/i18n/index.js
@@ -0,0 +1,16 @@
+import { createI18n } from 'vue-i18n';
+
+import { $local } from '../tools';
+import zh from './zh.json';
+import en from './en.json';
+const messages = { enUS: en, zhCN: zh };
+$local._lang = $local._lang !== 'zhCN' && $local._lang !== 'enUS' ? 'zhCN' : $local._lang;
+const i18n = createI18n({
+ locale: $local._lang, // set locale
+ fallbackLocale: 'zhCN', // set fallback locale
+ messages, // set locale messages
+});
+const $t = i18n.global.t;
+const $t2 = (cn, en) => (i18n.global.locale === 'zhCN' ? cn : en);
+
+export { i18n, $t, $t2 };
diff --git a/examples/react-site/src/i18n/zh.json b/examples/react-site/src/i18n/zh.json
new file mode 100644
index 000000000..e3a443359
--- /dev/null
+++ b/examples/react-site/src/i18n/zh.json
@@ -0,0 +1,23 @@
+{
+ "dark": "深色",
+ "light": "浅色",
+ "searchPlaceholder": "搜索",
+ "home": "首页",
+ "doc": "文档",
+ "component": "组件",
+ "common": "常规",
+ "apiPreference": "框架选择",
+ "apiTiny": "Vue",
+ "yan-shi": "演示",
+ "name": "名称",
+ "propType": "类型",
+ "defValue": "默认值",
+ "typeValue": "可选值",
+ "desc": "说明",
+ "hideCode": "收起代码",
+ "showCode": "显示代码",
+ "copyCode": "复制代码",
+ "doc-owner": "负责人",
+ "copyCodeOk": "复制成功",
+ "frameAngular": "Angular"
+}
diff --git a/examples/react-site/src/main.js b/examples/react-site/src/main.js
new file mode 100644
index 000000000..992d1fa8c
--- /dev/null
+++ b/examples/react-site/src/main.js
@@ -0,0 +1,32 @@
+import { createHead } from '@vueuse/head';
+import { createApp } from 'vue';
+import '@unocss/reset/eric-meyer.css';
+// markdown文件内代码高亮
+import 'prismjs/themes/prism.css';
+import 'uno.css';
+// markdown样式引用的是github-markdown.css
+import 'github-markdown-css/github-markdown.css';
+import './assets/index.less';
+// 覆盖默认的github markdown样式
+import './assets/custom-markdown.css';
+import './assets/custom-block.less';
+import { i18n } from './i18n/index';
+import { router } from './router';
+import App from './App.vue';
+import { $t, $t2 } from './i18n';
+import { $pub } from './tools';
+import demoConfig from '@demos/config.js';
+
+let app = createApp(App);
+app.config.performance = true;
+app
+ .use(router)
+ .use(i18n)
+ .use(createHead()) // 支持md修改title
+ .mixin({ methods: { $t, $t2, $pub } });
+
+if (typeof demoConfig.initApp === 'function') {
+ demoConfig.initApp(app);
+}
+
+app.mount('#app');
diff --git a/examples/react-site/src/menus.jsx b/examples/react-site/src/menus.jsx
new file mode 100644
index 000000000..4486d36a6
--- /dev/null
+++ b/examples/react-site/src/menus.jsx
@@ -0,0 +1,54 @@
+import { docMenus, cmpMenus } from '@demos/webdoc/menus.js';
+
+import { appData, $t2 } from './tools';
+
+/**
+ * 聚合doc / cmp 两个页面的所有菜单.
+ * 根据menus,生成总的menuOptions, doc的路由指向docs/:docId, 组件的路由指向components/:cmpId
+ * 1、docId 必须匹配doc_md中的文件名 , cmpId必须匹配 cmp_md 中的文件名
+ */
+const getTo = (route, key) => `${import.meta.env.VITE_CONTEXT}${route}${key}`;
+const getTitle = page => `${page.name} ${$t2(page.nameCn, '')}`;
+
+// 生成所有的菜单
+function genMenus() {
+ const standaloneOptions = [
+ {
+ key: 'overview',
+ label: () => (
+
+ 组件总览
+
+ ),
+ },
+ ];
+
+ const docOptions = docMenus.map(menu => ({
+ ...menu,
+ children: menu.children.map(page => ({
+ key: page.key,
+ label: () => (
+
+ {page.title}
+
+ ),
+ })),
+ }));
+ const cmpOptions = cmpMenus.map(menu => ({
+ ...menu,
+ children: menu.children.map(page => ({
+ key: page.key,
+ label: () => (
+
+
+ {page.name}
+ {appData.lang === 'zhCN' && {page.nameCn} }
+
+
+ ),
+ })),
+ }));
+ return [...standaloneOptions, ...docOptions, ...cmpOptions];
+}
+
+export { genMenus };
diff --git a/examples/react-site/src/router.js b/examples/react-site/src/router.js
new file mode 100644
index 000000000..e45b88226
--- /dev/null
+++ b/examples/react-site/src/router.js
@@ -0,0 +1,53 @@
+import { createRouter, createWebHistory } from 'vue-router';
+import Layout from '@/views/layout/layout.vue';
+import Components from '@/views/components/components.vue'
+import DemoPage from '@/views/components/demoPage.vue'
+import Docs from '@/views/docs/docs.vue'
+import Overview from '@/views/overview.vue'
+
+let routes = [
+ // 组件总览
+ {
+ path: import.meta.env.VITE_CONTEXT + 'overview',
+ component: Layout,
+ name: 'overview',
+ children: [{ path: '', component: Overview, meta: { title: '组件总览 | TinyVue' } }],
+ },
+ // 文档
+ {
+ path: import.meta.env.VITE_CONTEXT + 'docs/:docId',
+ component: Layout,
+ name: 'docs',
+ children: [{ path: '', component: Docs }],
+ },
+ // 组件
+ {
+ path: import.meta.env.VITE_CONTEXT + 'components/:cmpId',
+ component: Layout,
+ name: 'components',
+ children: [{ path: '', component: Components }],
+ },
+ //单组件示例
+ {
+ path: import.meta.env.VITE_CONTEXT + 'demoPage/:cmpId/:demoId',
+ component: DemoPage,
+ name: 'demoPage',
+ },
+ // 未匹配到目标地址时,进行路由重定向
+ {
+ path: '/:pathMatch(.*)*',
+ redirect: { path: import.meta.env.VITE_CONTEXT + 'overview' },
+ },
+];
+const router = createRouter({
+ history: createWebHistory(),
+ routes,
+});
+
+// 为浏览器添加title
+router.afterEach((to, from) => {
+ if (to.meta.title) {
+ document.title = to.meta.title;
+ }
+});
+export { router };
diff --git a/examples/react-site/src/tools/appData.js b/examples/react-site/src/tools/appData.js
new file mode 100644
index 000000000..3c71a674b
--- /dev/null
+++ b/examples/react-site/src/tools/appData.js
@@ -0,0 +1,29 @@
+import { reactive, computed, watchEffect } from 'vue';
+
+import { useAutoStore } from './storage';
+import { useMediaQuery } from './useMediaQuery';
+
+const appData = reactive({
+ lang: useAutoStore('local', '_lang', 'zhCN'),
+ theme: useAutoStore('local', '_theme', 'light'),
+ configMode: false,
+ configType: computed(() => 'tiny'),
+ bpState: useMediaQuery([640, 1024, 1280]).matches, // 3点4区间, bp0,bp1,bp2,bp3
+});
+let appFn = {
+ toggleLang() {
+ appData.lang = appData.lang === 'zhCN' ? 'enUS' : 'zhCN';
+ location.reload();
+ },
+ toggleTheme() {
+ appData.theme = appData.theme === 'light' ? 'dark' : 'light';
+ },
+};
+// 减少页面处理
+watchEffect(() => {
+ document.body.classList.remove('theme-light', 'theme-dark');
+ document.body.classList.add('theme-' + appData.theme);
+});
+// 为了和tiny-vue共享同一个响应变量
+window.appData = appData;
+export { appData, appFn };
diff --git a/examples/react-site/src/tools/index.js b/examples/react-site/src/tools/index.js
new file mode 100644
index 000000000..b7e62a92f
--- /dev/null
+++ b/examples/react-site/src/tools/index.js
@@ -0,0 +1,4 @@
+export * from './storage.js';
+export * from './utils.js';
+export * from '@/i18n/index';
+export * from './appData.js';
diff --git a/examples/react-site/src/tools/storage.js b/examples/react-site/src/tools/storage.js
new file mode 100644
index 000000000..0020a97d6
--- /dev/null
+++ b/examples/react-site/src/tools/storage.js
@@ -0,0 +1,69 @@
+import { ref, watch } from 'vue';
+
+function parse(str) {
+ if (str === null) return undefined;
+ const type = str[0];
+ const strVal = str.slice(1);
+ // 对象时,有可能是Date, 否则反解析后统一是对象
+ if (type === 'o' || type === 'b') {
+ let val = JSON.parse(strVal);
+ return typeof val === 'string' ? new Date(val) : val;
+ }
+ if (type === 'n') return +Number(strVal);
+ if (type === 's') return strVal;
+}
+
+// 带前缀的保存值
+function save(store, k, v) {
+ const type = typeof v;
+ store.setItem(k, type === 'object' ? JSON.stringify(v) : v);
+}
+
+/**
+ * 快速的保存值到 sessionStorage, localStorage.
+ * 支持基本类型,时间,数组,对象,null,不存在的键值返回undefined 。
+ * 不支持:Map,Set, 以及多级串联赋值,比如:$session.obj.name="abcd"
+ */
+function handler(storage) {
+ return {
+ get: function (target, propKey, receiver) {
+ return storage.getItem(propKey);
+ },
+ set: function (target, propKey, value) {
+ save(storage, propKey, value);
+ return true;
+ },
+ };
+}
+
+/** * 快速读写sessionStorage 示例: $session.abc="shen" */
+const $session = new Proxy({}, handler(sessionStorage));
+
+/** * 快速读写 localStorage 示例: $local.abc="shen" */
+const $local = new Proxy({}, handler(localStorage));
+
+/** * 全局共享值,刷新即丢失! 示例: $cache.abc="shen" */
+const $cache = {};
+
+const typeMatcher = { session: $session, local: $local, api: null };
+
+/**
+ * 用于记录用户行为,并保存到session,local 或api接口(api保存的功能还未实现)
+ * 示例:useAutoStore("session","key1")
+ * useAutoStore("session","key2",100)
+ * useAutoStore("session","key2",$session.key2 || 100)
+ * @param type 自动存储到的目标
+ * @param key 存储时的key
+ * @param defaultValue 默认值。
+ * @returns 响应式ref
+ */
+const useAutoStore = (type, key, defaultValue) => {
+ let refVar = ref(typeMatcher[type][key]);
+ watch(refVar, (curr, prev) => {
+ typeMatcher[type][key] = curr;
+ });
+ if (typeof refVar.value === 'undefined') refVar.value = defaultValue;
+ return refVar;
+};
+
+export { $session, $local, $cache, useAutoStore };
diff --git a/examples/react-site/src/tools/useMediaQuery.js b/examples/react-site/src/tools/useMediaQuery.js
new file mode 100644
index 000000000..b2522017d
--- /dev/null
+++ b/examples/react-site/src/tools/useMediaQuery.js
@@ -0,0 +1,29 @@
+import { reactive, nextTick } from 'vue';
+
+export function useMediaQuery(breakpoints, onChange) {
+ let matches = reactive({}); // 格式为: { bp0:false, bp1:true,bp2:false}
+ // 生成 query 表达式
+ let start = 1;
+ let querys = [];
+ breakpoints.forEach(bp => {
+ querys.push(`(min-width:${start}px) and (max-width:${bp - 1}px)`);
+ start = bp;
+ });
+ querys.push(`(min-width:${start}px)`);
+ let mqlList = querys.map(q => window.matchMedia(q));
+ // 添加所有监听, 通过Idx追踪位置
+ mqlList.forEach((mql, idx) => {
+ matches['bp' + idx] = mql.matches;
+ matches['range-bp' + idx] = querys[idx];
+ mql.fn = ev => {
+ matches['bp' + idx] = ev.matches;
+ ev.matches && nextTick(onChange);
+ };
+ mql.addEventListener('change', mql.fn);
+ });
+ function destory() {
+ mqlList.forEach(mql => mql.removeEventListener('change', mql.fn));
+ mqlList = null;
+ }
+ return { matches, destory };
+}
diff --git a/examples/react-site/src/tools/utils.js b/examples/react-site/src/tools/utils.js
new file mode 100644
index 000000000..1bb0016f8
--- /dev/null
+++ b/examples/react-site/src/tools/utils.js
@@ -0,0 +1,55 @@
+const baseUrl = import.meta.env.BASE_URL;
+
+/**
+ * json clone, 会丢失函数等。
+ * @param obj 普通对象或reactive对象
+ */
+const $clone = target => JSON.parse(JSON.stringify(target));
+
+/**
+ * 将目标字段分隔后,取相应位置的值
+ * @example $split("/project/home","/",-1) //取出home
+ * @param target 目标字符串
+ * @param splitor 分隔符
+ * @param pos 取数位置,可为-1,-2
+ */
+const $split = (target, splitor = '/', pos = 0) => target.split(splitor).slice(pos)[0];
+
+/**
+ * 延时函数
+ * @example $delay(300).then(()=>{ })
+ */
+const $delay = time => new Promise(resolve => setTimeout(resolve, time));
+
+/**
+ * 空闲函数
+ * @example $idle().then(()=>{ })
+ */
+const $idle = () => new Promise(resolve => (window.requestIdleCallback || window.requestAnimationFrame)(resolve));
+
+const $pub = url => {
+ // return baseUrl + url;
+ return `${baseUrl}/${url}`;
+};
+
+/**
+ * fetch组件库示例静态文件,包括markdown、示例源码和示例配置
+ * @param {string} path
+ * @returns
+ */
+const fetchDemosFile = path => {
+ return fetch(baseUrl + '/' + path, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'text/plain;charset=UTF-8',
+ },
+ }).then(res => {
+ if (res.ok) {
+ return res.text();
+ } else {
+ throw new Error(res.statusText);
+ }
+ });
+};
+
+export { $clone, $split, $delay, $idle, $pub, fetchDemosFile };
diff --git a/examples/react-site/src/views/components/cmpConfig.js b/examples/react-site/src/views/components/cmpConfig.js
new file mode 100644
index 000000000..06dfef56a
--- /dev/null
+++ b/examples/react-site/src/views/components/cmpConfig.js
@@ -0,0 +1,40 @@
+import { $split } from '@/tools';
+
+// 批量导入vue组件示例文件, 进行vue组件示例的渲染
+const vueFiles = import.meta.globEager('@demos/app/**/*.vue');
+const vueComponents = Object.create(null);
+for (const path in vueFiles) {
+ if (Object.prototype.hasOwnProperty.call(vueFiles, path)) {
+ const cmpId = $split(path, '/', -2);
+ const key = $split(path, '/', -1);
+ vueComponents[`${cmpId}/${key}`] = vueFiles[path].default;
+ }
+}
+
+const languageMap = {
+ js: 'javascript',
+ ts: 'javascript',
+ html: 'html',
+ vue: 'html',
+ css: 'css',
+ less: 'css',
+ scss: 'css',
+ sass: 'css',
+};
+
+const textColor = '#5073e5';
+const borderColor = '#d9dbdd';
+const themeOverrides = {
+ Tabs: {
+ tabTextColorActiveLine: textColor,
+ tabTextColorHoverLine: textColor,
+ barColor: textColor,
+ tabBorderColor: borderColor,
+ },
+};
+
+// 只有select组件需要select.faq.cn.md
+const faqMdConfig = {
+ select: true
+};
+export { languageMap, themeOverrides, faqMdConfig, vueComponents };
diff --git a/examples/react-site/src/views/components/components.vue b/examples/react-site/src/views/components/components.vue
new file mode 100644
index 000000000..7931da2d0
--- /dev/null
+++ b/examples/react-site/src/views/components/components.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
diff --git a/examples/react-site/src/views/components/componentsDetail.vue b/examples/react-site/src/views/components/componentsDetail.vue
new file mode 100644
index 000000000..2e21e4260
--- /dev/null
+++ b/examples/react-site/src/views/components/componentsDetail.vue
@@ -0,0 +1,361 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('yan-shi') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
API
+
+
+
+
{{ oneGroup.name }}
+
{{ oneGroup.type }}
+
+
+
+
+
+
+
+
+
+
FAQ
+
+
{{ $t('doc-owner') }} : {{ currJson.owner }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/src/views/components/componentsDetailTab.vue b/examples/react-site/src/views/components/componentsDetailTab.vue
new file mode 100644
index 000000000..dda432d04
--- /dev/null
+++ b/examples/react-site/src/views/components/componentsDetailTab.vue
@@ -0,0 +1,402 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('yan-shi') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
API
+
+
+
+
{{ oneGroup.name }}
+
{{ oneGroup.type }}
+
+
+
+
+
+
+
+
+
+ FAQ
+
+ {{ $t('doc-owner') }} : {{ currJson.owner }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/src/views/components/demo.vue b/examples/react-site/src/views/components/demo.vue
new file mode 100644
index 000000000..3a5b46999
--- /dev/null
+++ b/examples/react-site/src/views/components/demo.vue
@@ -0,0 +1,211 @@
+
+
+
+
+
+
{{ demo.name[langKey] }}
+
+
+
+
+
+ {{ copyTip }}
+
+
+
+
+
+ {{ demo.isOpen ? $t('hideCode') : $t('showCode') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/src/views/components/demoPage.vue b/examples/react-site/src/views/components/demoPage.vue
new file mode 100644
index 000000000..e9d8ec301
--- /dev/null
+++ b/examples/react-site/src/views/components/demoPage.vue
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
+
+
+ {{ copyTip }}
+
+
+
+
+
+ {{ demo.isOpen ? $t('hideCode') : $t('showCode') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/src/views/docs/docConfig.js b/examples/react-site/src/views/docs/docConfig.js
new file mode 100644
index 000000000..385a76cdf
--- /dev/null
+++ b/examples/react-site/src/views/docs/docConfig.js
@@ -0,0 +1,13 @@
+import { $split } from '@/tools/utils';
+// 从npm 仓库的位置,引入所有md. 统一用文件名做key
+const docMDs = {};
+const mds = import.meta.globEager('@demos/webdoc/**.md');
+
+for (const path in mds) {
+ if (Object.prototype.hasOwnProperty.call(mds, path)) {
+ const key = $split(path, '/', -1);
+ docMDs[key] = mds[path].default;
+ }
+}
+
+export default docMDs;
diff --git a/examples/react-site/src/views/docs/docs.vue b/examples/react-site/src/views/docs/docs.vue
new file mode 100644
index 000000000..0bcf6f84b
--- /dev/null
+++ b/examples/react-site/src/views/docs/docs.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
diff --git a/examples/react-site/src/views/layout/layout.vue b/examples/react-site/src/views/layout/layout.vue
new file mode 100644
index 000000000..c98b0356d
--- /dev/null
+++ b/examples/react-site/src/views/layout/layout.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/src/views/layout/layoutData.js b/examples/react-site/src/views/layout/layoutData.js
new file mode 100644
index 000000000..c559bf74b
--- /dev/null
+++ b/examples/react-site/src/views/layout/layoutData.js
@@ -0,0 +1,43 @@
+export function hiddenPanel() {
+ // 解决滚动页面时,下拉类组件面板与目标元素分离问题。为容器添加scroll事件,滚动页面,关闭下拉面板。
+ const demoContainerEle = document.getElementById('doc-layout').getElementsByClassName('n-layout-scroll-container')[0];
+ demoContainerEle?.addEventListener('scroll', () => {
+ const event = new CustomEvent('tiScroll', { bubbles: false, cancelable: true });
+ document.dispatchEvent(event);
+ });
+}
+
+// 主题色更新
+const backgroundColor = '#e9edfa';
+const textColor = '#5073e5';
+const borderFocus = '1px solid #5073e5';
+const boxShadowFocus = '0 0 0 2px rgba(80,115,229,0.2)';
+export const themeOverrides = {
+ Menu: {
+ itemTextColorActive: textColor,
+ itemColorActive: backgroundColor,
+ itemTextColorActiveHover: textColor,
+ itemColorHover: backgroundColor,
+ itemColorActiveHover: backgroundColor,
+ itemTextColorChildActive: textColor,
+ itemTextColorChildActiveHover: textColor,
+ arrowColorActive: textColor,
+ arrowColorActiveHover: textColor,
+ arrowColorChildActive: textColor,
+ arrowColorChildActiveHover: textColor,
+ },
+ Anchor: {
+ linkColor: backgroundColor,
+ linkTextColorHover: textColor,
+ linkTextColorActive: textColor,
+ linkTextColorPressed: textColor,
+ railColorActive: textColor,
+ },
+ Input: {
+ caretColor: textColor,
+ borderHover: borderFocus,
+ borderFocus: borderFocus,
+ loadingColor: textColor,
+ boxShadowFocus: boxShadowFocus,
+ },
+};
diff --git a/examples/react-site/src/views/overview.vue b/examples/react-site/src/views/overview.vue
new file mode 100644
index 000000000..52ceb5c75
--- /dev/null
+++ b/examples/react-site/src/views/overview.vue
@@ -0,0 +1,143 @@
+
+
+
+
组件总览
+
TinyVue 为 Web 应用提供了丰富的基础 UI 组件,我们还将持续探索企业级应用的最佳 UI 实践,欢迎尝试使用 TinyVue。
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ menu.label }}
+ {{ menu.children.length }}
+
+
+
+
+
+
+
+ {{ cell.name }}
+ {{ cell.nameCn }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/react-site/src/webcomps/alert/base.js b/examples/react-site/src/webcomps/alert/base.js
new file mode 100644
index 000000000..166c5f437
--- /dev/null
+++ b/examples/react-site/src/webcomps/alert/base.js
@@ -0,0 +1,104 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Alert } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var base = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(base2, _HTMLElement);
+ function base2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = base2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Alert, {
+ description: "type 为默认值 success"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "error",
+ description: "type 为 error"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "info",
+ description: "type 为 info"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "warning",
+ description: "type 为 warning"
+ })));
+ };
+ return base2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ base as default
+};
diff --git a/examples/react-site/src/webcomps/alert/center.js b/examples/react-site/src/webcomps/alert/center.js
new file mode 100644
index 000000000..3028aa2f5
--- /dev/null
+++ b/examples/react-site/src/webcomps/alert/center.js
@@ -0,0 +1,96 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Alert } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var center = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(center2, _HTMLElement);
+ function center2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = center2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Alert, {
+ center: true,
+ description: "文字居中"
+ })));
+ };
+ return center2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ center as default
+};
diff --git a/examples/react-site/src/webcomps/alert/size.js b/examples/react-site/src/webcomps/alert/size.js
new file mode 100644
index 000000000..be5a123d9
--- /dev/null
+++ b/examples/react-site/src/webcomps/alert/size.js
@@ -0,0 +1,99 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Alert } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var size = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(size2, _HTMLElement);
+ function size2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = size2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Alert, {
+ size: "normal",
+ description: "size 为 normal"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ size: "large",
+ title: "size 为 large"
+ })));
+ };
+ return size2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ size as default
+};
diff --git a/examples/react-site/src/webcomps/alert/title.js b/examples/react-site/src/webcomps/alert/title.js
new file mode 100644
index 000000000..728a14675
--- /dev/null
+++ b/examples/react-site/src/webcomps/alert/title.js
@@ -0,0 +1,103 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Alert } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var title = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(title2, _HTMLElement);
+ function title2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = title2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Alert, {
+ size: "large",
+ title: "通过属性设置自定义 title"
+ }), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement(Alert, {
+ size: "large",
+ slots: {
+ title: function title3() {
+ return "通过 slot 设置自定义 title";
+ }
+ }
+ })));
+ };
+ return title2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ title as default
+};
diff --git a/examples/react-site/src/webcomps/alert/type.js b/examples/react-site/src/webcomps/alert/type.js
new file mode 100644
index 000000000..43257e1fd
--- /dev/null
+++ b/examples/react-site/src/webcomps/alert/type.js
@@ -0,0 +1,107 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Alert } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var type = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(type2, _HTMLElement);
+ function type2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = type2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Alert, {
+ description: "type 为默认值 success"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "info",
+ description: "type 为 info"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "error",
+ description: "type 为 error"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "success",
+ description: "type 为 success"
+ }), /* @__PURE__ */ React.createElement(Alert, {
+ type: "warning",
+ description: "type 为 warning"
+ })));
+ };
+ return type2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ type as default
+};
diff --git a/examples/react-site/src/webcomps/button/button-click-webcomp.js b/examples/react-site/src/webcomps/button/button-click-webcomp.js
new file mode 100644
index 000000000..b095c94b9
--- /dev/null
+++ b/examples/react-site/src/webcomps/button/button-click-webcomp.js
@@ -0,0 +1,93 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Button } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var buttonClickWebcomp = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(buttonClickWebcomp2, _HTMLElement);
+ function buttonClickWebcomp2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = buttonClickWebcomp2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Button, null, "默认按钮")));
+ };
+ return buttonClickWebcomp2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ buttonClickWebcomp as default
+};
diff --git a/examples/react-site/src/webcomps/button/button-round-webcomp.js b/examples/react-site/src/webcomps/button/button-round-webcomp.js
new file mode 100644
index 000000000..d29fcfe2e
--- /dev/null
+++ b/examples/react-site/src/webcomps/button/button-round-webcomp.js
@@ -0,0 +1,96 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Button } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var buttonRoundWebcomp = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(buttonRoundWebcomp2, _HTMLElement);
+ function buttonRoundWebcomp2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = buttonRoundWebcomp2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Button, {
+ round: true,
+ type: "primary"
+ }, "主要按钮")));
+ };
+ return buttonRoundWebcomp2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ buttonRoundWebcomp as default
+};
diff --git a/examples/react-site/src/webcomps/button/button-type-webcomp.js b/examples/react-site/src/webcomps/button/button-type-webcomp.js
new file mode 100644
index 000000000..ffa971a04
--- /dev/null
+++ b/examples/react-site/src/webcomps/button/button-type-webcomp.js
@@ -0,0 +1,95 @@
+import React from 'react'
+function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ _setPrototypeOf(subClass, superClass);
+}
+function _wrapNativeSuper(Class) {
+ var _cache = typeof Map === "function" ? /* @__PURE__ */ new Map() : void 0;
+ _wrapNativeSuper = function _wrapNativeSuper2(Class2) {
+ if (Class2 === null || !_isNativeFunction(Class2))
+ return Class2;
+ if (typeof Class2 !== "function") {
+ throw new TypeError("Super expression must either be null or a function");
+ }
+ if (typeof _cache !== "undefined") {
+ if (_cache.has(Class2))
+ return _cache.get(Class2);
+ _cache.set(Class2, Wrapper);
+ }
+ function Wrapper() {
+ return _construct(Class2, arguments, _getPrototypeOf(this).constructor);
+ }
+ Wrapper.prototype = Object.create(Class2.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } });
+ return _setPrototypeOf(Wrapper, Class2);
+ };
+ return _wrapNativeSuper(Class);
+}
+function _construct(Parent, args, Class) {
+ if (_isNativeReflectConstruct()) {
+ _construct = Reflect.construct.bind();
+ } else {
+ _construct = function _construct2(Parent2, args2, Class2) {
+ var a = [null];
+ a.push.apply(a, args2);
+ var Constructor = Function.bind.apply(Parent2, a);
+ var instance = new Constructor();
+ if (Class2)
+ _setPrototypeOf(instance, Class2.prototype);
+ return instance;
+ };
+ }
+ return _construct.apply(null, arguments);
+}
+function _isNativeReflectConstruct() {
+ if (typeof Reflect === "undefined" || !Reflect.construct)
+ return false;
+ if (Reflect.construct.sham)
+ return false;
+ if (typeof Proxy === "function")
+ return true;
+ try {
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {
+ }));
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+function _isNativeFunction(fn) {
+ return Function.toString.call(fn).indexOf("[native code]") !== -1;
+}
+function _setPrototypeOf(o, p) {
+ _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf2(o2, p2) {
+ o2.__proto__ = p2;
+ return o2;
+ };
+ return _setPrototypeOf(o, p);
+}
+function _getPrototypeOf(o) {
+ _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf2(o2) {
+ return o2.__proto__ || Object.getPrototypeOf(o2);
+ };
+ return _getPrototypeOf(o);
+}
+import { Button } from "@pe-3/react";
+import ReactDOM from "react-dom/client";
+function App(props) {
+ return /* @__PURE__ */ React.createElement("div", null, props.children);
+}
+var buttonTypeWebcomp = /* @__PURE__ */ function(_HTMLElement) {
+ _inheritsLoose(buttonTypeWebcomp2, _HTMLElement);
+ function buttonTypeWebcomp2() {
+ return _HTMLElement.apply(this, arguments) || this;
+ }
+ var _proto = buttonTypeWebcomp2.prototype;
+ _proto.connectedCallback = function connectedCallback() {
+ ReactDOM.createRoot(this).render(/* @__PURE__ */ React.createElement(App, null, /* @__PURE__ */ React.createElement(Button, {
+ type: "success"
+ }, "成功按钮")));
+ };
+ return buttonTypeWebcomp2;
+}(/* @__PURE__ */ _wrapNativeSuper(HTMLElement));
+export {
+ buttonTypeWebcomp as default
+};
diff --git a/examples/react-site/tiny-uno/index.js b/examples/react-site/tiny-uno/index.js
new file mode 100644
index 000000000..4df7dab19
--- /dev/null
+++ b/examples/react-site/tiny-uno/index.js
@@ -0,0 +1,56 @@
+// 定制脚本的参数
+const defaultOption = {
+ prefix: '',
+ isRem: false,
+ breakpoints: {
+ xs: '0px',
+ sm: '640px',
+ md: '1024px',
+ lg: '1280px',
+ },
+};
+// rules
+import border from './rules/border';
+import color from './rules/color';
+import font from './rules/font';
+import layout from './rules/layout';
+import size from './rules/size';
+import transform from './rules/transform';
+import utils from './rules/utils';
+import animate from './rules/animate';
+
+// variants
+import child from './variants/child';
+import hover from './variants/hover';
+import important from './variants/important';
+import mediaquery from './variants/mediaquery';
+import range from './variants/range';
+import select from './variants/select';
+import prefixBuilder from './variants/prefix';
+
+// preflights
+import preflights from './preflights';
+
+// 每一项 opt={rules:[],shortcuts:[]}
+function merge(options, ...rules) {
+ let ret = { rules: [], shortcuts: [] };
+ rules.forEach(ruler => {
+ let rule = ruler(options);
+ ret.rules = ret.rules.concat(rule.rules);
+ ret.shortcuts = ret.shortcuts.concat(rule.shortcuts);
+ });
+ return ret;
+}
+export default options => {
+ const tempOptions = { ...defaultOption, ...options };
+ let prefix = tempOptions.prefix;
+ return {
+ name: 'preset-tinyuno',
+ ...merge(options, border, color, font, layout, size, transform, utils, animate),
+ variants: [prefixBuilder(prefix), child, hover, important, mediaquery, range, select],
+ theme: {
+ breakpoints: tempOptions.breakpoints,
+ },
+ preflights,
+ };
+};
diff --git a/examples/react-site/tiny-uno/preflights.js b/examples/react-site/tiny-uno/preflights.js
new file mode 100644
index 000000000..5aca1ba5b
--- /dev/null
+++ b/examples/react-site/tiny-uno/preflights.js
@@ -0,0 +1,7 @@
+export default [
+ {
+ getCSS: () => `:root {
+
+ }`,
+ },
+];
diff --git a/examples/react-site/tiny-uno/rules/animate.js b/examples/react-site/tiny-uno/rules/animate.js
new file mode 100644
index 000000000..895cd0293
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/animate.js
@@ -0,0 +1,61 @@
+// 所有的动画效果
+import { toEscapedSelector as e } from 'unocss';
+const _s = {
+ run: 'running',
+ pause: 'paused', //
+
+ none: 'none',
+ both: 'both',
+ stay: 'forwards',
+ start: 'backwards',
+
+ ease: 'ease',
+ in: 'ease-in',
+ out: 'ease-out',
+ inout: 'ease-in-out',
+ linear: 'linear',
+};
+import * as keyframes from './keyframes/index';
+export default function builder(option) {
+ return {
+ rules: [
+ // 动画效果及动画影响属性 ani-bounce-width#0px#-100px
+ // 操作的属性只能是纯数值+单位或无单位。只能操作1个属性
+ [
+ /^ani-(line|bounce|shake)-(\w+)#(-?\w+)#(-?\w+)$/,
+ ([, name, prop, from, to], { rawSelector, currentSelector, variantHandlers, theme }) => {
+ return `
+ ${e(currentSelector)}{
+ animation-name:${name + prop};
+ }
+ @keyframes ${name + prop} {
+ ${keyframes[name](prop, from, to)}
+ }
+ `;
+ },
+ ],
+ // 时长 last ani-l#3s ani-last#3s 必填项
+ [/^ani-(l|last)#(\d+\.?\d*)s$/, ([, tag, time]) => ({ 'animation-duration': `${time}s` })],
+ // 延迟 delay ani-d#3s ani-d#-3s ani-delay#-3s 默认0
+ [/^ani-(d|delay)#(-?\d+\.?\d*)s$/, ([, tag, time]) => ({ 'animation-delay': `${time}s` })],
+ // 次数 count ani-c#3 ani-count#3.5 ani-c#infinite ani-c#keep 默认1 小数表示执行一半就结束
+ [/^ani-(c|count)#(\d+\.?\d*)$/, ([, tag, time]) => ({ 'animation-iteration-count': `${time}` })],
+ [/^ani-(c|count)#(infinite|keep)$/, ([, tag]) => ({ 'animation-iteration-count': 'infinite' })],
+
+ // 是否循环往返 ani-round 添加则往返。 默认为: ani-normal 。不添加则每次循环,即每个周期都从头开始
+ ['ani-round', { 'animation-direction': 'alternate' }],
+ ['ani-normal', { 'animation-direction': 'normal' }],
+
+ // 结束时停住位置, ani-stop-stay ani-stop-none 默认为: 结束时,清除动画属性. 有delay时间时, stop-start表示,未开始时,位置于0%
+ [/^ani-stop-(stay|none|both|start)$/, ([, pos]) => ({ 'animation-fill-mode': `${_s[pos]}` })],
+
+ // 动画函数 ani-fun-ease 默认值 ease
+ [/^ani-(f|fun|function)-(ease|in|out|inout|linear)$/, ([, tag, pos]) => ({ 'animation-timing-function': `${_s[pos]}` })],
+ // ani-fun-step4
+ [/^ani-(f|fun|function)-step(\d)$/, ([, tag, num]) => ({ 'animation-timing-function': `steps(${num},start)` })],
+ // 动画开关, ani-run ani-pause 不添加则结束时,清除动画属性 【ani-stop-start还不行】
+ [/^ani-(run|pause)$/, ([, pos]) => ({ 'animation-play-state': `${_s[pos]}` })],
+ ],
+ shortcuts: [],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/border.js b/examples/react-site/tiny-uno/rules/border.js
new file mode 100644
index 000000000..2738705f3
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/border.js
@@ -0,0 +1,35 @@
+// shortkey的 字典变量
+const _s = {
+ a: '', // all
+ r: '-right',
+ l: '-left',
+ b: '-bottom',
+ t: '-top',
+
+ ws: 'var(--border-width) var(--border-style) ', // width & style
+};
+
+export default function builder(option) {
+ const isRem = !!option.isRem;
+ const $t = num => (isRem ? `${num}rem` : `${num}px`);
+ return {
+ rules: [
+ // 边框 b-a b-b b-r b-t b-l b-a-primary b-a#ff0000
+ [/^b-([arlbt])$/, ([, pos]) => ({ [`border${_s[pos]}`]: _s.ws + 'var(--border-color)' })],
+ [/^b-([arlbt])-(\w+)$/, ([, pos, color]) => ({ [`border${_s[pos]}`]: _s.ws + `var(--${color})` })],
+ [/^b-([arlbt])#(\w+)$/, ([, pos, color]) => ({ [`border${_s[pos]}`]: _s.ws + `#${color}` })],
+ // 边框样式 bs-dotted bs-double
+ [/^bs-(none|dotted|dashed|solid|double|groove|ridge|inset|outset)$/, ([, style]) => ({ 'border-style': style })],
+ [/^br-(\d+)$/, ([, val]) => ({ 'border-radius': $t(val) })],
+ // 无边框 nb-a nb-b nb-r nb-t nb-l
+ [/^nb-([arlbt])$/, ([, pos]) => ({ [`border${_s[pos]}`]: 'none' })],
+
+ ['br-sm', { 'border-radius': 'var(--radius-sm)' }],
+ ['br-lg', { 'border-radius': 'var(--radius-lg)' }],
+ ['br-circle', { 'border-radius': '50%' }],
+ ['bs-sm', { 'box-shadow': 'var(--shadow-sm)' }],
+ ['bs-lg', { 'box-shadow': 'var(--shadow-lg)' }],
+ ],
+ shortcuts: [],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/color.js b/examples/react-site/tiny-uno/rules/color.js
new file mode 100644
index 000000000..2597ab017
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/color.js
@@ -0,0 +1,12 @@
+// shortkey的 字典变量
+const _s = { c: 'color', bg: 'background-color' };
+export default function builder(option) {
+ return {
+ rules: [
+ // 颜色 #不建议使用 c-black bg-primary c#123456
+ [/^(c|bg)-(\w+)$/, ([, attr, color]) => ({ [`${_s[attr]}`]: `var(--${color})` })],
+ [/^(c|bg)#(\w+)$/, ([, attr, color]) => ({ [`${_s[attr]}`]: `#${color}` })],
+ ],
+ shortcuts: [],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/font.js b/examples/react-site/tiny-uno/rules/font.js
new file mode 100644
index 000000000..962cc7c37
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/font.js
@@ -0,0 +1,39 @@
+// shortkey的 字典变量
+const _s = {
+ f: 'font-size',
+ lh: 'line-height',
+
+ bold: 'bolder',
+ thin: 'lighter',
+ normal: 'normal',
+
+ underline: 'underline',
+ overline: 'overline',
+ through: 'line-through',
+};
+
+export default function builder(option) {
+ const isRem = !!option.isRem;
+ const $t = (num) => (isRem ? `${num}rem` : `${num}px`);
+ return {
+ rules: [
+ // 字体与行高 f12 lh20
+ [/^(f|lh)(\d+)$/, ([, attr, num]) => ({ [`${_s[attr]}`]: $t(num) })],
+ // 字体粗细 fw-bold fw-700
+ [/^fw-(bold|thin|normal)$/, ([, dir]) => ({ 'font-weight': `${_s[dir]}` })],
+ [/^fw-(\d+)$/, ([, val]) => ({ 'font-weight': val })],
+ // 文字对齐 text-right text-underline text-overline
+ [/^text-(right|left|center)$/, ([, dir]) => ({ 'text-align': dir })],
+ // 文字上下划线 solid|double|dotted|dashed|wavy
+ // text-underline text-overlinewavy text-overline-
+ [
+ /^text-(underline|overline|through)-?(solid|double|dotted|dashed|wavy)?$/,
+ ([, dir, style]) => ({ 'text-decoration': `${dir} ${style || ''}` }),
+ ],
+ // 文字阴影 ts-sm ts-lg
+ ['ts-sm', { 'text-shadow': 'var(--text-shadow-sm)' }],
+ ['ts-lg', { 'text-shadow': 'var(--text-shadow-lg)' }],
+ ],
+ shortcuts: [],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/keyframes/bounce.js b/examples/react-site/tiny-uno/rules/keyframes/bounce.js
new file mode 100644
index 000000000..6829cd1fd
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/keyframes/bounce.js
@@ -0,0 +1,20 @@
+import help from './help';
+
+export function bounce(prop, from, to) {
+ // eslint-disable-next-line no-unused-vars
+ const { min, max, unit, mid } = help(from, to);
+ return `
+ from,20%,53%,80%,to {
+ ${prop}: ${from};
+ }
+ 40%,43% {
+ ${prop}: ${to};
+ }
+ 70%{
+ ${prop}: ${mid(50) + unit};
+ }
+ 90%{
+ ${prop}: ${mid(13.2) + unit};
+ }
+ `;
+}
diff --git a/examples/react-site/tiny-uno/rules/keyframes/help.js b/examples/react-site/tiny-uno/rules/keyframes/help.js
new file mode 100644
index 000000000..867d18fa6
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/keyframes/help.js
@@ -0,0 +1,19 @@
+/**
+ *
+ * @param {string} from
+ * @param {string} to
+ * @returns Object:{ min,max,unit}
+ */
+export default function (from, to) {
+ let min = parseFloat(from);
+ let max = parseFloat(to);
+ let unit = from.replace(min.toString(), '');
+
+ let mid = v => ((max - min) / 100) * v + min;
+ return {
+ min,
+ max,
+ unit,
+ mid,
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/keyframes/index.js b/examples/react-site/tiny-uno/rules/keyframes/index.js
new file mode 100644
index 000000000..06617a8c8
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/keyframes/index.js
@@ -0,0 +1,3 @@
+export * from './line';
+export * from './bounce';
+export * from './shake';
diff --git a/examples/react-site/tiny-uno/rules/keyframes/line.js b/examples/react-site/tiny-uno/rules/keyframes/line.js
new file mode 100644
index 000000000..18be99a71
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/keyframes/line.js
@@ -0,0 +1,10 @@
+export function line(prop, from, to) {
+ return `
+from {
+ ${prop}: ${from};
+}
+to {
+ ${prop}: ${to};
+}
+`;
+}
diff --git a/examples/react-site/tiny-uno/rules/keyframes/shake.js b/examples/react-site/tiny-uno/rules/keyframes/shake.js
new file mode 100644
index 000000000..4d23fae11
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/keyframes/shake.js
@@ -0,0 +1,19 @@
+import help from './help';
+/**
+ * 指定范围的上下10%的幅度,进行摆动
+ */
+export function shake(prop, from, to) {
+ // eslint-disable-next-line no-unused-vars
+ const { min, max, unit, mid } = help(from, to);
+ return `
+ from,to {
+ ${prop}: ${from};
+ }
+ 10%,30%,50%,70%,90%{
+ ${prop}: ${mid(-10) + unit};
+ }
+ 20%,40%,60%,80% {
+ ${prop}: ${mid(110) + unit};
+ }
+ `;
+}
diff --git a/examples/react-site/tiny-uno/rules/layout.js b/examples/react-site/tiny-uno/rules/layout.js
new file mode 100644
index 000000000..c5098383e
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/layout.js
@@ -0,0 +1,55 @@
+// shortkey的 字典变量
+const _s = {
+ none: 'none',
+ block: 'block',
+ inline: 'inline',
+ flex: 'flex',
+ grid: 'grid',
+ ib: 'inline-block',
+ if: 'inline-flex',
+ ig: 'inline-grid',
+
+ abs: 'absolute',
+ rel: 'relative',
+ fixed: 'fixed',
+ sticky: 'sticky',
+ static: 'static',
+
+ r: 'row',
+ c: 'column',
+ center: 'center',
+ start: 'flex-start',
+ end: 'flex-end',
+ around: 'space-around',
+ between: 'space-between',
+ evenly: 'space-evenly',
+ stretch: 'stretch',
+};
+
+export default function builder(option) {
+ return {
+ rules: [
+ // display d-none, d-block d-flex d-inline d-grid d-ib d-if d-ig
+ [/^d-(none|block|flex|inline|grid|ib|if|ig)$/, ([, pos]) => ({ display: `${_s[pos]}` })],
+ // position abs rel fixed sticky static
+ [/^(rel|abs|fixed|sticky|static)$/, ([, pos]) => ({ position: `${_s[pos]}` })],
+ // 绝对定位 abs-0 fixed-0
+ ['abs-0', { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }],
+ ['fixed-0', { position: 'fixed', left: 0, right: 0, top: 0, bottom: 0 }],
+
+ // flex布局 父元素: f-r f-c f-center f-pos-between f-box-stretch f-wrap
+ [/^f-([rc])$/, ([, dir]) => ({ display: 'flex', 'flex-direction': `${_s[dir]}` })],
+ [/^f-pos-(start|center|end|around|between|evenly|stretch)$/, ([, dir]) => ({ 'justify-content': `${_s[dir]}` })],
+ [/^f-box-(start|center|end|stretch)$/, ([, dir]) => ({ 'align-items': `${_s[dir]}` })],
+ [/^f-(wrap|nowrap)$/, ([, dir]) => ({ 'flex-wrap': dir })],
+
+ // 子元素: fi-1 fi-4
+ [/^fi-(\d+)$/, ([, num]) => ({ flex: `${num}` })],
+ ],
+ shortcuts: [
+ {
+ 'f-center': 'd-flex f-pos-center f-box-center',
+ },
+ ],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/size.js b/examples/react-site/tiny-uno/rules/size.js
new file mode 100644
index 000000000..a02f93e8d
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/size.js
@@ -0,0 +1,43 @@
+// shortkey的 字典变量
+const _s = {
+ m: 'margin',
+ p: 'padding',
+
+ w: 'width',
+ h: 'height',
+
+ r: '-right',
+ l: '-left',
+ b: '-bottom',
+ t: '-top',
+};
+
+export default function builder(option) {
+ const isRem = !!option.isRem;
+ const $t = num => (isRem ? `${num}rem` : `${num}px`);
+ return {
+ rules: [
+ // 高宽, 字体,行高 f12 lh20 w200 h200 box78
+ [/^(w|h)(\d+)$/, ([, attr, num]) => ({ [`${_s[attr]}`]: $t(num) })],
+ // 高宽百分比 wp33 hp50
+ [/^(w|h)p(\d+)$/, ([, attr, num]) => ({ [`${_s[attr]}`]: `${num}%` })],
+
+ // 内外边距 *可负* m10 mr10 mt-10 mx20 my-10 p10 pr10
+ [/^([mp])([rlbt]?)(-?\d+)$/, ([, attr, pos, num]) => ({ [`${_s[attr]}${pos ? _s[pos] : ''}`]: $t(num) })],
+ // 内外边距auto m-auto mt-auto mx-auto
+ [/^([mp])([rlbt]?)-auto$/, ([, attr, pos]) => ({ [`${_s[attr]}${pos ? _s[pos] : ''}`]: 'auto' })],
+
+ // abs 定位时 *可负* left0 right50 bottom-20 top-unset
+ [/^(left|top|right|bottom)(-?\d+)$/, ([, pos, num]) => ({ [`${pos}`]: $t(num) })],
+ [/^(left|top|right|bottom)-(auto|unset)$/, ([, pos, val]) => ({ [`${pos}`]: val })],
+ ],
+ shortcuts: [
+ [/^(m|p)x(-?\d+)$/, ([, t, c]) => `${t}l${c} ${t}r${c}`],
+ [/^(m|p)y(-?\d+)$/, ([, t, c]) => `${t}t${c} ${t}b${c}`],
+
+ [/^(m|p)x-auto$/, ([, t, c]) => `${t}l-auto ${t}r-auto`],
+ [/^(m|p)y-auto$/, ([, t, c]) => `${t}t-auto ${t}b-auto`],
+ [/^box(\d+)$/, ([, w]) => `w${w} h${w}`],
+ ],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/transform.js b/examples/react-site/tiny-uno/rules/transform.js
new file mode 100644
index 000000000..41e9671d1
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/transform.js
@@ -0,0 +1,32 @@
+// shortkey的 字典变量
+const _s = { c: 'center', l: 'left', t: 'top', r: 'right', b: 'bottom' };
+
+export default function builder(option) {
+ const isRem = !!option.isRem;
+ const $t = num => (isRem ? `${num}rem` : `${num}px`);
+ return {
+ rules: [
+ // 启用过渡 trans
+ ['trans', { transition: 'all var(--trans-time)' }],
+ // transform中心点 to-c to-lt to-t
+ [/^to-([cltrb])([cltrb]?)$/, ([, dir1, dir2]) => ({ 'transform-origin': `${_s[dir1]} ${dir2 ? _s[dir2] : ''}` })],
+
+ // 旋转 【可负可小数】 tr15 tr45.25 tr-45.25
+ [/^tr(-?\d+\.?\d*)$/, ([, ang]) => ({ transform: `rotateZ(${ang}deg)` })],
+
+ // 缩放 【可小数】 ts2 ts3.5 h:ts3.5
+ [/^ts(\d+\.?\d*)$/, ([, ang]) => ({ transform: `scale(${ang})` })], // 匹配小数
+
+ // 平移 【可负可小数】 tt-10 tt-10#10 tt-10.5#12.5
+ [
+ /^tt(-?\d+\.?\d*)(#-?\d+\.?\d*)?$/,
+ ([, x, y]) => {
+ let target;
+ if (y) target = y.slice(1); // 去#
+ return { transform: `translate(${$t(x)},${target ? $t(target) : '0'})` };
+ },
+ ],
+ ],
+ shortcuts: [],
+ };
+}
diff --git a/examples/react-site/tiny-uno/rules/utils.js b/examples/react-site/tiny-uno/rules/utils.js
new file mode 100644
index 000000000..afad034c0
--- /dev/null
+++ b/examples/react-site/tiny-uno/rules/utils.js
@@ -0,0 +1,45 @@
+// shortkey的 字典变量
+const _s = {
+ def: 'default',
+ hand: 'pointer',
+ disable: 'not-allowed',
+};
+const clamp = (v, min, max) => Math.min(Math.max(v, min), max);
+
+export default function builder(option) {
+ return {
+ rules: [
+ // 省略号 ellipsis ellipsis2 ellipsis3
+ ['ellipsis', { overflow: 'hidden', ' text-overflow': 'ellipsis', 'white-space': 'nowrap' }],
+ [
+ /^ellipsis(\d+)$/,
+ ([, num]) => ({
+ display: '-webkit-box',
+ overflow: 'hidden',
+ '-webkit-line-clamp': num,
+ ' -webkit-box-orient': 'vertical',
+ 'overflow-wrap': 'anywhere',
+ }),
+ ],
+ // 光标样式 cur-hand
+ [/^cur-(def|hand|disable)$/, ([, shape]) => ({ cursor: `${_s[shape]}` })],
+ // overflow of-auto ofx-hidden ofy-scroll
+ [/^of(\w?)-(auto|scroll|hidden|visible)$/, ([, axis, mode]) => ({ [`overflow${axis ? '-' + axis : ''}`]: mode })],
+ // 图片填充 img-cover
+ [/^img-(cover|contain|fill)$/, ([, mode]) => ({ 'object-fit': mode })],
+ // z-index z100
+ [/^z(-?\d+)$/, ([, num]) => ({ 'z-index': num })],
+ // 选择相关 noselect allselect nomouse 参见选择变体: select:c-primary select:bg-dark
+ ['noselect', { 'user-select': 'none' }],
+ ['allselect', { 'user-select': 'all' }],
+ ['noevent', { 'pointer-events': 'none' }],
+ // 可见性 hide show
+ ['hide', { visibility: 'hidden' }],
+ ['show', { visibility: 'visible' }],
+ // 透明度 op100
+ [/^op(\d+)$/, ([, val]) => ({ opacity: clamp(val / 100, 0, 1) })],
+ ['decoration-none', { 'text-decoration': 'none' }],
+ ],
+ shortcuts: [],
+ };
+}
diff --git a/examples/react-site/tiny-uno/variants/child.js b/examples/react-site/tiny-uno/variants/child.js
new file mode 100644
index 000000000..05e22209e
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/child.js
@@ -0,0 +1,9 @@
+// "child:" child:f16 child:box32
+export default (matcher) => {
+ if (!matcher.startsWith('child:')) return matcher;
+ return {
+ matcher: matcher.slice(6),
+ selector: (s) => `${s}>*`,
+ body: (s)=>s
+ };
+};
diff --git a/examples/react-site/tiny-uno/variants/hover.js b/examples/react-site/tiny-uno/variants/hover.js
new file mode 100644
index 000000000..b055551b9
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/hover.js
@@ -0,0 +1,12 @@
+// hover: h:c-error
+export default (matcher) => {
+ if (!matcher.startsWith('h:')) return matcher;
+ return {
+ matcher: matcher.slice(2),
+ selector: (s) => `${s}:hover`,
+ body: (body) => {
+ body.push(['transition', 'all var(--trans-time)']);
+ return body;
+ },
+ };
+};
diff --git a/examples/react-site/tiny-uno/variants/important.js b/examples/react-site/tiny-uno/variants/important.js
new file mode 100644
index 000000000..939efa65b
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/important.js
@@ -0,0 +1,13 @@
+// ! !c-error
+export default matcher => {
+ if (!matcher.startsWith('!')) return matcher;
+ return {
+ matcher: matcher.slice(1),
+ body: body => {
+ body.forEach(e => {
+ e[1] && (e[1] += ' !important');
+ });
+ return body;
+ },
+ };
+};
diff --git a/examples/react-site/tiny-uno/variants/mediaquery.js b/examples/react-site/tiny-uno/variants/mediaquery.js
new file mode 100644
index 000000000..9c8f57fa6
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/mediaquery.js
@@ -0,0 +1,14 @@
+// mediaQuery , xs:w120 sm:w60 md:w80 lg:w120 md:w240 lg:w480
+export default (matcher, context) => {
+ const reg = '^(xs|sm|md|lg):';
+ const [, mediaQuery = ''] = matcher.match(new RegExp(reg)) ?? [];
+
+ if (!mediaQuery) {
+ return matcher;
+ }
+ let bp = context.theme.breakpoints;
+ return {
+ matcher: matcher.replace(/^.+?:(.*)$/, '$1'),
+ parent: `@media (min-width: ${bp[mediaQuery]})`,
+ };
+};
diff --git a/examples/react-site/tiny-uno/variants/prefix.js b/examples/react-site/tiny-uno/variants/prefix.js
new file mode 100644
index 000000000..2be1a8d0d
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/prefix.js
@@ -0,0 +1,8 @@
+// 这是一个特殊的变体。 当用户需要使用缀时,传入前缀,返回一个变体函数
+export default prefix => matcher => {
+ if (!matcher.startsWith(prefix)) return 'nomatch';
+ return {
+ matcher: matcher.slice(prefix.length),
+ selector: s => `${s}`,
+ };
+};
diff --git a/examples/react-site/tiny-uno/variants/range.js b/examples/react-site/tiny-uno/variants/range.js
new file mode 100644
index 000000000..a4d92515e
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/range.js
@@ -0,0 +1,12 @@
+// range: 配合 range 使用 range:c-error
+export default (matcher) => {
+ if (!matcher.startsWith('range:')) return matcher;
+ return {
+ matcher: matcher.slice(6),
+ selector: (s) => `.range:hover ${s}`,
+ body: (body) => {
+ body.push(['transition', 'all var(--trans-time)']);
+ return body;
+ },
+ };
+};
diff --git a/examples/react-site/tiny-uno/variants/select.js b/examples/react-site/tiny-uno/variants/select.js
new file mode 100644
index 000000000..9dfb4f920
--- /dev/null
+++ b/examples/react-site/tiny-uno/variants/select.js
@@ -0,0 +1,14 @@
+// select:c-primary select:bg-black ::selection只可以应用于少数的CSS属性:color, background
+export default matcher => {
+ if (!matcher.startsWith('select:')) return matcher;
+ return {
+ matcher: matcher.slice(7),
+ selector: s => `${s} ::selection`,
+ body: body => {
+ body.forEach(e => {
+ e[1] && (e[1] += '!important');
+ });
+ return body;
+ },
+ };
+};
diff --git a/examples/react-site/tiny.config.js b/examples/react-site/tiny.config.js
new file mode 100644
index 000000000..b8a2c21da
--- /dev/null
+++ b/examples/react-site/tiny.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+ toolkit: '@opentiny/tiny-toolkit-docs',
+ tasks: {
+ start: [
+ // 此处配置tiny start的前置执行命令,会在套件的start命令前执行
+ // { command: 'node scripts/test.js' }
+ ],
+ build: [
+ // 此处配置tiny build的前置执行命令,会在套件的build命令前执行
+ // { command: 'node scripts/test.js' }
+ ],
+ },
+};
diff --git a/examples/react-site/tsconfig.json b/examples/react-site/tsconfig.json
new file mode 100644
index 000000000..1b9567171
--- /dev/null
+++ b/examples/react-site/tsconfig.json
@@ -0,0 +1,31 @@
+{
+ "compilerOptions": {
+ "allowJs": true ,
+ "noImplicitAny": false,
+ "baseUrl": ".",
+ "paths": {
+ "@opentiny/react-*": [
+ "packages/react-*",
+ "packages/react/src/*"
+ ],
+ "@opentiny/vue-renderless*": [
+ "packages/renderless/src*"
+ ]
+ },
+ "types": [
+ "node",
+ "vite/client"
+ ],
+ "jsx": "react-jsx"
+ },
+ "include": [
+ "packages/**/*.ts",
+ "packages/**/*.tsx",
+ "packages/**/*.jsx",
+ ],
+ "exclude": [
+ "**/node_modules",
+ "**/dist*",
+ "**/*.md"
+ ]
+}
\ No newline at end of file
diff --git a/examples/react-site/uno.config.js b/examples/react-site/uno.config.js
new file mode 100644
index 000000000..f6c62910b
--- /dev/null
+++ b/examples/react-site/uno.config.js
@@ -0,0 +1,35 @@
+import UnocssIcons from '@unocss/preset-icons';
+import presetTinyUno from './tiny-uno/index';
+
+export default {
+ include: [/\.js$/, /\.ts$/, /\.vue$/, /\.html$/, /\.jsx$/, /\.tsx$/], // 增加js ,ts扫描
+ presets: [
+ presetTinyUno({
+ isRem: false,
+ prefix: '',
+ // breakpoints: {
+ // xs: '0px',
+ // sm: '100px',
+ // md: '204px',
+ // lg: '3001px',
+ // },
+ }),
+ // 非常多的图标,默认可以引用,https://icones.js.org/
+ UnocssIcons({
+ prefix: 'i-',
+ extraProperties: {
+ display: 'inline-block',
+ },
+ collections: {
+ ti: {
+ copy: ' ',
+ code: ' ',
+ codeslash:
+ ' ',
+ check:
+ ' ',
+ },
+ },
+ }),
+ ],
+};
diff --git a/examples/react-site/vite.config.js b/examples/react-site/vite.config.js
new file mode 100644
index 000000000..7708ea8d1
--- /dev/null
+++ b/examples/react-site/vite.config.js
@@ -0,0 +1,63 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import Unocss from 'unocss/vite';
+import createHtmlPlugin from 'vite-plugin-html';
+import path from 'path';
+import UnoCssConfig from './uno.config';
+import AutoComponents from 'unplugin-vue-components/vite';
+import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import Markdown from 'vite-plugin-md';
+import { MdExt, mdInstall } from './md.extend.config';
+
+export default defineConfig({
+ envDir: './env',
+ base: '/tiny-react',
+ plugins: [
+ vue({
+ include: [/\.vue$/, /\.md$/],
+ }),
+ vueJsx({
+ include: [/\.js$/, /\.jsx$/, /\.ts$/, /\.tsx$/],
+ }),
+ createHtmlPlugin(),
+ // 支持md转为vue组件: https://github.com/antfu/vite-plugin-md#configuration--options
+ Markdown({
+ headEnabled: true,
+ markdownItOptions: {
+ html: true,
+ linkify: true,
+ typographer: true,
+ },
+ markdownItSetup(md) {
+ mdInstall(md);
+ },
+ markdownItUses: MdExt,
+ }),
+ Unocss(UnoCssConfig),
+ // 自动导入和项目组件 https://github.com/antfu/unplugin-vue-components#configuration
+ AutoComponents({
+ resolvers: [NaiveUiResolver()],
+ extensions: ['vue', 'md'],
+ include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
+ }),
+ ],
+ define: {
+ 'process.env': { ...process.env },
+ },
+ resolve: {
+ alias: {
+ '@': path.resolve('src'),
+ '@demos': path.resolve('demos'),
+ '@demo': path.resolve('src/views/components/demo.vue')
+ },
+ },
+ server: {
+ host: '0.0.0.0',
+ port: 3101,
+ fs: {
+ strict: false,
+ allow: ['..'],
+ },
+ },
+});
diff --git a/internals/cli/package.json b/internals/cli/package.json
index 39ae83917..6cd6e2cea 100644
--- a/internals/cli/package.json
+++ b/internals/cli/package.json
@@ -5,9 +5,9 @@
"version": "1.0.5-mf.0",
"description": "internal-cli",
"devDependencies": {
- "@opentiny-internal/unplugin-virtual-template": "workspace:*",
"@babel/core": "^7.5.5",
"@babel/preset-env": "7.16.7",
+ "@opentiny-internal/unplugin-virtual-template": "workspace:*",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^24.0.1",
"@types/fs-extra": "^11.0.1",
@@ -46,13 +46,18 @@
"sync-icons": "esno src/commands/create/sync-icons.ts",
"create:icon-saas": "esno src/index.ts create:icon-saas",
"clean:build": "esno src/commands/clean.ts",
- "create:mapping": "esno src/commands/create/create-mapping.ts"
+ "create:mapping": "esno src/commands/create/create-mapping.ts",
+ "build:entry-react": "esno src/index.ts build:entry-react",
+ "create:mapping-react": "esno src/commands/create/create-mapping-react.ts",
+ "build:react": "esno src/index.ts build:react"
},
"dependencies": {
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
"esno": "^0.16.3",
"fast-glob": "^3.2.12",
+ "rollup-plugin-replace": "^2.2.0",
"vite-plugin-dts": "^3.0.0",
+ "vite-plugin-svgr": "^3.2.0",
"vite-svg-loader": "^3.6.0"
}
}
diff --git a/internals/cli/src/commands/build/build-entry-react.ts b/internals/cli/src/commands/build/build-entry-react.ts
new file mode 100644
index 000000000..d87f73ece
--- /dev/null
+++ b/internals/cli/src/commands/build/build-entry-react.ts
@@ -0,0 +1,103 @@
+/**
+ * 生成入口文件,包括 pc.js / mobile.js / mobile-first.js / index.js
+ */
+import fs from 'fs-extra'
+import { EOL as endOfLine } from 'node:os'
+import {
+ pathFromWorkspaceRoot,
+ capitalizeKebabCase,
+ prettierFormat,
+ logGreen
+} from '../../shared/utils'
+import { getAllModules } from './build-ui-react'
+import handlebarsRender from './handlebars.render'
+
+const version = (({ key }) => {
+ const packageJSON = fs.readJSONSync(pathFromWorkspaceRoot('packages/react/package.json'))
+ const packageJsonOption = packageJSON[key] || packageJSON
+
+ return packageJsonOption
+})({ key: 'version' })
+
+const outputDir = 'packages/react'
+
+const fileNames = {
+ all: 'index.ts',
+ pc: 'pc.ts',
+ mobile: 'mobile.ts',
+ 'mobile-first': 'mobile-first.ts'
+}
+
+function getMainTemplate({ mode }) {
+ return `{{{include}}}
+ import { $prefix } from '@opentiny/react-common'
+
+ const components = [{{{components}}}]
+
+ export const version = '${version}'
+
+ export {
+ {{{components}}}
+ }
+
+ export default {
+ {{{components}}}
+ } as any
+ `
+}
+
+function getComponents(mode) {
+ const modules = getAllModules()
+
+ const components = modules
+ .filter((item) => item.type === 'component')
+ .filter((item) => mode === 'all' || !item.mode || item.mode.includes(mode))
+
+ return components
+}
+
+function createEntry(mode) {
+ const OUTPUT_PATH = pathFromWorkspaceRoot(outputDir, fileNames[mode]);
+ const MAIN_TEMPLATE = getMainTemplate({ mode })
+ const includeTemplate: string[] = []
+ const componentsTemplate: string[] = []
+ const components = getComponents(mode)
+ const PKG_PATH = pathFromWorkspaceRoot(outputDir, 'package.json')
+ const PKGContent = fs.readJSONSync(PKG_PATH)
+ const PKGDeps = {
+ '@opentiny/react-common': 'workspace:~'
+ }
+
+ components.forEach((item) => {
+ const component = capitalizeKebabCase(item.name)
+ PKGDeps[item.importName] = 'workspace:~'
+ componentsTemplate.push(` ${component}`)
+ const importName = mode === 'all' ? item.importName : `${item.importName}/src/${mode}`
+ includeTemplate.push(`import ${item.name} from '${importName}'`)
+ })
+
+ if (mode === 'all') {
+ PKGContent.dependencies = PKGDeps
+ fs.writeFileSync(PKG_PATH, JSON.stringify(PKGContent, null, 2))
+ }
+
+ const template = handlebarsRender({
+ template: MAIN_TEMPLATE,
+ data: {
+ include: includeTemplate.join(endOfLine),
+ components: componentsTemplate.join(',' + endOfLine)
+ }
+ })
+
+ const output = prettierFormat({ str: template })
+
+ fs.writeFileSync(OUTPUT_PATH, output)
+}
+
+export function buildEntryReact() {
+ ['all', 'pc', 'mobile', 'mobile-first'].forEach(createEntry)
+
+ logGreen(
+ `npm run build:entry done. [${outputDir}/index.ts,${outputDir}/pc.ts,${outputDir}/mobile.ts,${outputDir}/mobile-first.ts]`
+ )
+}
diff --git a/internals/cli/src/commands/build/build-ui-react.ts b/internals/cli/src/commands/build/build-ui-react.ts
new file mode 100644
index 000000000..8a1a1000b
--- /dev/null
+++ b/internals/cli/src/commands/build/build-ui-react.ts
@@ -0,0 +1,518 @@
+import { logGreen, kebabCase, capitalizeKebabCase } from '../../shared/utils'
+import { pathFromWorkspaceRoot } from '../../shared/utils'
+import fg from 'fast-glob'
+import path from 'node:path'
+import { build, defineConfig } from 'vite'
+import { getBabelOutputPlugin } from '@rollup/plugin-babel'
+import { external } from '../../shared/config'
+import type { Plugin, NormalizedOutputOptions, OutputBundle } from 'rollup'
+import fs from 'fs-extra'
+import { sync as findUpSync } from 'find-up'
+import svgr from 'vite-plugin-svgr'
+import { requireModules } from './build-ui'
+import replace from 'rollup-plugin-replace'
+
+const moduleMap = require(pathFromWorkspaceRoot('packages/modules-react.json'))
+type mode = 'pc' | 'mobile' | 'mobile-first'
+
+const pathFromPackages = (...args) => pathFromWorkspaceRoot('packages', ...args)
+
+let scopeName = '@opentiny'
+let buildVersion = '1.0.0'
+
+export interface Module {
+ /** 源码路径,如 vue/src/button/index.ts */
+ path: string
+ /** 模块类型,可选 component, template, module */
+ type: 'component' | 'template' | 'module'
+ /** 是否排除构建,例如组件尚未开发完,设置 true */
+ exclude?: boolean
+ /** 组件类型支持的模式 */
+ mode?: mode[]
+ /** 模块名称,如 Button */
+ name: string
+ /** 模块构建物路径,如 vue/button/lib/index */
+ libPath: string
+ /** 模块名,如 @opentiny/vue/vue/lib/button/src,@deprecated */
+ libName?: string
+ /** 模块的npm包名,如 @opentiny/vue-button */
+ importName: string
+ /** 构建物文件名,@deprecated */
+ tmpName?: string
+ /** 全局变量名,如 TinyButton */
+ global?: string
+ /** 组件名的大写形态,如 Button */
+ UpperName?: string
+ /** 组件名的小写形态,如 button */
+ LowerName?: string
+ /** 模块的路径 */
+ parentDir?: string[]
+}
+
+function getEntryTasks(): Module[] {
+ // 读取TinyVue组件库入口文件
+ return ['index', 'pc', 'mobile'].map((mode) => ({
+ path: `react/${mode}.ts`,
+ dtsRoot: true,
+ libPath: `react/${mode}`,
+ type: 'module',
+ name: kebabCase({ str: `${scopeName}/react` }),
+ global: capitalizeKebabCase('opentinyReact'),
+ importName: `${scopeName}/react`
+ }))
+}
+
+export function getAllModules(): Module[] {
+ return getSortModules({ filterIntercept: () => true })
+}
+
+const getSortModules = ({ filterIntercept }: { filterIntercept: Function; }) => {
+ let modules: Module[] = []
+ let componentCount = 0
+ const importName = `${scopeName}/react`
+ Object.entries(moduleMap).forEach(([key, module]) => {
+ let component = module as Module
+
+ component.name = key
+ // filterIntercept过滤筛选命令行传过来的组件名称,只输出命令行传递过来的组件
+ if (filterIntercept(component) === true && component.exclude !== true) {
+ const dirs = component.path.split('/')
+
+ // 这段逻辑暂时没有用到
+ const componentName = dirs.slice(1, dirs.indexOf('src'))
+ // UpperName: Todo
+ component.UpperName = capitalizeKebabCase(componentName.pop() ?? '')
+
+ // LowerName: todo
+ component.LowerName = kebabCase({ str: component.UpperName })
+
+ // 工程的父文件夹
+ component.parentDir = componentName
+
+ // libPath: 'packages/todo/dist/pc.ts' 组件输出路径
+ component.libPath = component.path
+ .replace('react/src/', 'packages/')
+ .replace('react-common/src/', 'packages/common/')
+ .replace('react-locale/src/', 'packages/locale/')
+ .replace('react-icon/src/', 'packages/icon/')
+ .replace('/index.ts', '/src/index.js')
+ .replace('/src/', '/dist/lib/')
+ .replace('.jsx', '.js')
+ .replace('.tsx', '.js')
+
+ // libName: '@opentiny/vue/todo/pc'
+ component.libName = component.libPath
+ .replace('packages/', '')
+ .replace('/index', '')
+ .replace('.js', '')
+ .replace('/dist/', '/')
+ .replace(/\/lib$/, '')
+
+ // 处理子目录
+ if (componentName.length) {
+ component.libName = component.libName.replace(componentName.join('/'), '').replace(/^\//, '')
+ }
+
+ // importName: '@opentiny/vue-tag/pc'
+ component.importName = `${scopeName}/react` + '-' + component.libName
+
+ // libName: '@opentiny/vue/todo/pc'
+ component.libName = importName + '/' + component.libName
+
+ // tmpName: 'pc'
+ component.tmpName = component.libPath.replace('.ts', '').split('/').slice(-1)[0]
+
+ // global: 'TinyTodoPc'
+ component.global = 'Tiny' + key
+
+ component.importName = `${scopeName}/react-${kebabCase({ str: key })}`
+
+ // "vue-common/src/index.ts" ==> "vue-common/lib/index"
+ if (component.type === 'module') {
+ component.libPath = component.path.replace('/src/', '/lib/').replace('index.ts', 'index')
+ }
+
+ // "vue/src/button/index.ts" ==> "button/lib/index"
+ if (component.type === 'component') {
+ component.libPath = component.path.replace('react/src/', '').replace('/index.ts', '/lib/index')
+ }
+
+ // "vue/src/button/src/mobile-first.vue" ==> "button/lib/mobile-first"
+ if (component.type === 'template') {
+ component.libPath = component.path.replace('react/src/', '').replace('/src/', '/lib/').replace(/\..+$/, '')
+ }
+
+ modules.push(component)
+ }
+
+ component.type === 'component' && componentCount++
+ })
+
+ return modules
+}
+
+/**
+ * 根据指定条件搜索原始模块列表
+ * @private
+ * @param {Function} filterIntercept 搜索条件
+ */
+const getModules = (filterIntercept: Function) => {
+ let modules = {}
+
+ if (typeof filterIntercept === 'function') {
+ for (const key in moduleMap) {
+ const component = moduleMap[key]
+
+ if (filterIntercept(component) === true && component.exclude !== true) {
+ modules[key] = component
+ }
+ }
+ } else {
+ modules = moduleMap
+ }
+
+ return modules
+}
+
+const getByName = ({
+ name,
+ isSort = true,
+ inversion = false,
+ isOriginal = false
+}: {
+ name: string
+ isSort: boolean
+ inversion?: boolean
+ isOriginal?: boolean
+}) => {
+ const callback = (item) => {
+ const result = new RegExp(`/${name}/|^react-${name}/`).test(item.path)
+ return inversion ? !result : result
+ }
+
+ return isOriginal ? getModules(callback) : getSortModules({ filterIntercept: callback })
+}
+
+function getTasks(names: string[]) {
+ // 没有指定组件,则全量构建
+ if (names.length === 0) {
+ return [...getAllModules(), ...getEntryTasks()]
+ }
+
+ return names
+ .map((name) =>
+ getByName({
+ name: kebabCase({ str: name.replace('@opentiny/react-', '') }),
+ isSort: false
+ })
+ )
+ .flat()
+}
+
+const getAllIcons = () => {
+ const entries = fg.sync('react-icon*/src/*', { cwd: pathFromWorkspaceRoot('packages'), onlyDirectories: true })
+
+ return entries.map((item) => {
+ const name = path.basename(item)
+
+ return {
+ path: item + '/index.ts',
+ libPath: item.replace('/src/', '/lib/'),
+ type: 'component',
+ componentType: 'icon',
+ name: kebabCase({ str: name }),
+ global: capitalizeKebabCase(name),
+ importName: '@opentiny/react-' + item
+ } as Module
+ })
+}
+
+function toEntry(libs) {
+ return libs.reduce((result, { libPath, path: file }) => {
+ const tLibPath = libPath.replace('-lib/', '/lib/')
+ result[tLibPath] = pathFromPackages(file)
+ return result
+ }, {})
+}
+
+function toTsInclued(libs) {
+ return new Set(
+ libs
+ .filter((item) => ['module', 'component'].includes(item.type))
+ .map((lib) => `packages/${lib.dtsRoot ? lib.path : path.dirname(lib.path)}`)
+ )
+}
+
+function generatePackageJson({ beforeWriteFile }): Plugin {
+ return {
+ name: 'opentiny-vue:generate-package-json',
+ generateBundle(output: NormalizedOutputOptions, bundle: OutputBundle) {
+ const cache = {}
+ Object.entries(bundle).forEach(([, item]) => {
+ // 主入口文件, button/index.ts, common/src/index.ts
+ if (item.type === 'chunk' && /\/index\.(js|ts)/.test(item.facadeModuleId!)) {
+ // 从源文件中往上查找最近的 package.json 文件
+ const packageJsonFile = findUpSync('package.json', { cwd: item.facadeModuleId! })
+
+ if (!packageJsonFile) return
+
+ if (cache[packageJsonFile]) return
+
+ let packageJson
+ try {
+ packageJson = JSON.parse(fs.readFileSync(packageJsonFile, { encoding: 'utf-8' }))
+ } catch { }
+
+ const { filePath, content } = beforeWriteFile(path.dirname(item.fileName), packageJson)
+
+ if (content) {
+ this.emitFile({
+ type: 'asset',
+ fileName: `${filePath}/package.json`,
+ source: typeof content === 'string' ? content : JSON.stringify(content, null, 2)
+ })
+ }
+
+ const changelogFile = path.join(path.dirname(packageJsonFile), 'CHANGELOG.md')
+ if (fs.existsSync(changelogFile)) {
+ this.emitFile({
+ type: 'asset',
+ fileName: `${filePath}/CHANGELOG.md`,
+ source: fs.readFileSync(changelogFile, { encoding: 'utf-8' })
+ })
+ }
+
+ cache[packageJsonFile] = true
+ }
+ })
+ }
+ }
+}
+
+const getComponentAlias = (alias = {}) => {
+ getAllModules().forEach((item) => {
+ if (item.type === 'component')
+ alias[item.importName] = pathFromWorkspaceRoot('packages', item.path)
+ })
+ return alias
+}
+
+const getReactPlugins = (reactVersion: string) => {
+ const pluginMap = {
+ '18': () => {
+ const react18Plugin = requireModules('examples/react-docs/node_modules/@vitejs/plugin-react')
+ const react18SvgPlugin = svgr
+
+ return [react18Plugin(), react18SvgPlugin()]
+ }
+ }
+
+ return pluginMap[reactVersion]()
+}
+
+function getBaseConfig({
+ dts,
+ dtsInclude
+}) {
+ return defineConfig({
+ publicDir: false,
+ resolve: {
+ extensions: ['.js', '.ts', '.tsx', '.jsx'],
+ },
+ define: {
+ 'process.env.BUILD_TARGET': JSON.stringify('component')
+ },
+ plugins: [
+ ...getReactPlugins('18'),
+ generatePackageJson({
+ beforeWriteFile: (filePath, content) => {
+ const dependencies: any = {}
+
+ Object.entries(content.dependencies).forEach(([key, value]) => {
+ // 只替换 react 系的依赖,方便调试
+ // 其他公用的依赖,vue 之前可能发过包
+ const newKey = key.replace('@opentiny/react', `${scopeName}/react`)
+ if ((value as string).includes('workspace:~')) {
+ dependencies[newKey] = '*'
+ }
+ else {
+ dependencies[newKey] = value
+ }
+ })
+
+ if (filePath.includes('react-common')) {
+ dependencies.react = '18.2.0'
+ }
+
+ // 如果是主入口或者svg图标则直接指向相同路径
+ if (
+ filePath === 'react' ||
+ filePath === 'react-icon'
+ ) {
+ content.main = './index.js'
+ content.module = './index.js'
+ } else {
+ content.main = './lib/index.js'
+ content.module = './lib/index.js'
+ }
+
+ content.version = buildVersion
+ content.dependencies = dependencies
+ content.name = content.name.replace('@opentiny/react', `${scopeName}/react`)
+
+ delete content.devDependencies
+ delete content.private
+ delete content.exports
+
+ return {
+ filePath: filePath.replace(/[\\/]lib$/, ''),
+ content
+ }
+ }
+ })
+ ]
+ })
+}
+
+async function batchBuild({
+ tasks,
+ formats,
+ message,
+ emptyOutDir,
+ dts,
+ outDir
+}) {
+ if (tasks.length === 0) return
+ logGreen(`====== 开始构建 ${message} ======`)
+ const entry = toEntry(tasks)
+ const dtsInclude = toTsInclued(tasks)
+
+ await build({
+ ...getBaseConfig({ dts, dtsInclude }),
+ configFile: false,
+ build: {
+ outDir,
+ emptyOutDir,
+ minify: false,
+ rollupOptions: {
+ plugins: [
+ getBabelOutputPlugin({
+ presets: [['@babel/preset-env', { loose: true, modules: false }]]
+ }) as any,
+ replace({
+ '.less': '.css'
+ }),
+ {
+ name: 'replace-scope',
+ transform(code) {
+ if (scopeName === '@opentiny') return code
+
+ let modifiedCode = code
+ while (modifiedCode.match(/@opentiny\/react/g)) {
+ modifiedCode = modifiedCode.replace('@opentiny/react', `${scopeName}/react`)
+ }
+ return {
+ code: modifiedCode
+ }
+ }
+ }
+ ],
+ output: {
+ strict: false,
+ manualChunks: {}
+ },
+ external: (source, importer, isResolved) => {
+ // vite打包入口文件或者没有解析过得包不能排除依赖
+ if (isResolved || !importer) {
+ return false
+ }
+
+ // 子图标排除周边引用, 这里注意不要排除svg图标
+ if (/react-icon\/.+\/index/.test(importer)) {
+ return !/\.svg/.test(source)
+ }
+
+ // @opentiny/vue 总入口,需要排除所有依赖
+ if (/react\/(index|pc|mobile|mobile-first)\.ts$/.test(importer)) {
+ return true
+ }
+
+ if ([
+ 'react',
+ 'react/jsx-runtime'
+ ].includes(source)) {
+ return true
+ }
+
+ if (source.indexOf(scopeName) === 0) {
+ return true
+ }
+
+ return external(source)
+ },
+ },
+ lib: {
+ entry,
+ formats,
+ fileName: (format, entryName) => `${entryName}.js`
+ }
+ }
+ })
+}
+
+async function batchBuildAll({
+ tasks,
+ formats,
+ message,
+ emptyOutDir,
+ dts,
+ npmScope
+}) {
+ const rootDir = pathFromPackages('')
+ const outDir = path.resolve(rootDir, `dist-react/${npmScope}`)
+ await batchBuild({
+ tasks,
+ formats,
+ message,
+ emptyOutDir,
+ dts,
+ outDir
+ })
+}
+
+export async function buildReact(
+ names: string[] = [],
+ {
+ buildTarget = '1.0.0',
+ formats = ['es'],
+ clean = false,
+ dts = true,
+ scope = '@opentiny'
+ }
+) {
+ scopeName = scope
+ buildVersion = buildTarget
+ // 要构建的模块
+ let tasks = getTasks(names)
+
+ // 如果指定了打包icon或者没有传入任何组件
+ if (names.some((name) => name.includes('icon')) || !names.length) {
+ tasks.push(...getAllIcons())
+ }
+
+ // 构建 @opentiny/react
+ if (names.some((name) => [`${scopeName}/react`, 'react'].includes(name))) {
+ tasks.push(...getEntryTasks())
+ }
+
+ const message = `TINY for react: ${JSON.stringify(names.length ? names : '全量')}`
+
+ await batchBuildAll({
+ tasks,
+ formats,
+ message,
+ emptyOutDir: clean,
+ dts,
+ npmScope: scope
+ })
+}
diff --git a/internals/cli/src/commands/build/build-ui.ts b/internals/cli/src/commands/build/build-ui.ts
index 9d6df5e1f..b67f24cde 100644
--- a/internals/cli/src/commands/build/build-ui.ts
+++ b/internals/cli/src/commands/build/build-ui.ts
@@ -384,4 +384,4 @@ export async function buildUi(
// 确保只运行一次
emptyOutDir = false
}
-}
+}
\ No newline at end of file
diff --git a/internals/cli/src/commands/build/index.ts b/internals/cli/src/commands/build/index.ts
index 0cbffa566..dd8175971 100644
--- a/internals/cli/src/commands/build/index.ts
+++ b/internals/cli/src/commands/build/index.ts
@@ -1,3 +1,5 @@
export * from './build-ui'
export * from './build-entry'
export * from './build-runtime'
+export * from './build-ui-react'
+export * from './build-entry-react'
\ No newline at end of file
diff --git a/internals/cli/src/commands/create/common-mapping-react.json b/internals/cli/src/commands/create/common-mapping-react.json
new file mode 100644
index 000000000..033028027
--- /dev/null
+++ b/internals/cli/src/commands/create/common-mapping-react.json
@@ -0,0 +1,12 @@
+{
+ "Icon": {
+ "path": "react-icon/index.ts",
+ "type": "module",
+ "exclude": false
+ },
+ "Common": {
+ "path": "react-common/src/index.ts",
+ "type": "module",
+ "exclude": false
+ }
+}
\ No newline at end of file
diff --git a/internals/cli/src/commands/create/create-mapping-react.ts b/internals/cli/src/commands/create/create-mapping-react.ts
new file mode 100644
index 000000000..ce4d7836a
--- /dev/null
+++ b/internals/cli/src/commands/create/create-mapping-react.ts
@@ -0,0 +1,111 @@
+import { capitalize, walkFileTree, pathFromWorkspaceRoot, logGreen, prettierFormat } from '../../shared/utils'
+import { quickSort } from '../../shared/module-utils'
+import path from 'node:path'
+import fs from 'fs-extra'
+import commonMappingReact from './common-mapping-react.json'
+
+const getBuildEntryFile = (file, dirs, subPath) => {
+ // 模板文件(pc|mobile|mobile-first)需要同级目录有index.ts文件才能成为打包入口
+ const isTemplatePath = dirs.includes('index.ts')
+ const isMainEntry = file.includes('index') && dirs.includes('package.json')
+ const isPcEntry = file.includes('pc.') && subPath.includes(`src${path.sep}pc.`) && isTemplatePath
+ const isMobileEntry = file.includes('mobile.') && subPath.includes(`src${path.sep}mobile.`) && isTemplatePath
+ const isMobileFirstEntry =
+ file.includes('mobile-first.') && subPath.includes(`src${path.sep}mobile-first.`) && isTemplatePath
+ return {
+ isBuildEntryFile: isMainEntry || isPcEntry || isMobileEntry || isMobileFirstEntry,
+ isMainEntry,
+ isPcEntry,
+ isMobileEntry,
+ isMobileFirstEntry
+ }
+}
+
+const tempMap = {
+ 'pc.jsx': 'pc',
+ 'mobile.jsx': 'mobile',
+ 'mobile-first.jsx': 'mobile-first',
+ 'pc.tsx': 'pc',
+ 'mobile.tsx': 'mobile',
+ 'mobile-first.tsx': 'mobile-first'
+}
+
+const getTemplateName = (currentPaths, entryObj) => {
+ const entryMaps = {
+ isPcEntry: 'Pc',
+ isMobileEntry: 'Mobile',
+ isMobileFirstEntry: 'MobileFirst',
+ isMainEntry: ''
+ }
+ const mapKey = Object.keys(entryObj).filter((item) => entryObj[item] && item !== 'isBuildEntryFile')[0]
+ const subFix = entryMaps[mapKey]
+ return `${currentPaths.split('-').map(capitalize).join('')}${subFix}`
+}
+
+export const writeModuleMap = (moduleMap) => {
+ fs.writeFileSync(
+ pathFromWorkspaceRoot('packages/modules-react.json'),
+ prettierFormat({
+ str: typeof moduleMap === 'string' ? moduleMap : JSON.stringify(moduleMap),
+ options: {
+ parser: 'json',
+ printWidth: 10
+ }
+ })
+ )
+}
+
+function makeReactModules() {
+ const templates = { ...commonMappingReact }
+
+ walkFileTree({
+ isDeep: true,
+ dirPath: pathFromWorkspaceRoot('packages/react/src'),
+ fileFilter({ file }) {
+ return !/node_modules/.test(file)
+ },
+ callback({ file, subPath, dirs, isDirectory }) {
+ const entryObj = getBuildEntryFile(file, dirs, subPath)
+ const mode: string[] = []
+
+ if (entryObj.isMainEntry && dirs.includes('src')) {
+ const srcPath = subPath.replace(file, 'src')
+ const srcFiles = fs.readdirSync(srcPath) || []
+ srcFiles.forEach((item) => {
+ if (tempMap[item]) {
+ mode.push(tempMap[item])
+ }
+ })
+ }
+
+ if (entryObj.isBuildEntryFile) {
+ const modulePath = subPath.slice(subPath.lastIndexOf(`react${path.sep}src`)).replaceAll(path.sep, '/')
+ const matchArr = modulePath.match(/.+\/(.+?)\/(index\.ts|src\/pc\.|src\/mobile\.|src\/mobile-first\.)/)
+ if (matchArr?.[1]) {
+ const compName = getTemplateName(matchArr[1], entryObj)
+ templates[compName] = {
+ path: modulePath,
+ type: entryObj.isMainEntry ? 'component' : 'template',
+ exclude: false
+ }
+ if (mode.length > 0) {
+ templates[compName].mode = mode
+ }
+ }
+ }
+ }
+ })
+
+ const modulesJson = quickSort({ sortData: templates, returnType: 'object' })
+
+ writeModuleMap(modulesJson)
+}
+
+try {
+ makeReactModules()
+
+ logGreen('npm run create:mapping-react done.')
+}
+catch (e) {
+ console.log(e)
+}
\ No newline at end of file
diff --git a/internals/cli/src/index.ts b/internals/cli/src/index.ts
index b25defad5..b4d3a63dc 100644
--- a/internals/cli/src/index.ts
+++ b/internals/cli/src/index.ts
@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { Command, Option } from 'commander'
import { createIconSaas } from './commands/create/index.js'
-import { buildUi, buildEntry, buildRuntime } from './commands/build'
+import { buildUi, buildEntry, buildRuntime, buildReact, buildEntryReact } from './commands/build'
import { releaseAurora } from './commands/release/releaseAurora'
const program = new Command()
@@ -10,6 +10,8 @@ program.command('release:aurora').description('转换为aurora的包').action(re
program.command('create:icon-saas').description('同步生成 icon-saas').action(createIconSaas)
+program.command('build:entry-react').description('生成 react 组件库入口').action(buildEntryReact)
+
program.command('build:entry').description('生成组件库入口').action(buildEntry)
program
@@ -31,4 +33,15 @@ program
.option('-m, --min', '是否压缩输出文件', false)
.action(buildRuntime)
+program
+ .command('build:react')
+ .description('打包 react 组件库')
+ .argument('[names...]', '构建指定组件,如 button alert;不指定则构建全量组件')
+ .addOption(new Option('-f --formats ', '目标格式,默认 ["es"]').choices(['es', 'cjs']))
+ .addOption(new Option('-t --build-target ', '组件的目标版本'))
+ .option('-s, --scope ', 'npm scope,默认是 opentiny,会以 @opentiny 发布到 npm')
+ .option('-c, --clean', '清空构建目录')
+ .option('--no-dts', '不生成 dts')
+ .action(buildReact)
+
program.parse()
diff --git a/internals/cli/src/shared/config.ts b/internals/cli/src/shared/config.ts
index 3dd9cc996..61d82ca0a 100644
--- a/internals/cli/src/shared/config.ts
+++ b/internals/cli/src/shared/config.ts
@@ -16,4 +16,4 @@ const external = (deps) => {
return EXTENERAL.includes(deps) || /^@opentiny[\\/]|@originjs|echarts|cropperjs|@better-scroll|crypto-js/.test(deps)
}
-export { external }
+export { external }
\ No newline at end of file
diff --git a/package.json b/package.json
index 9a6c66feb..c934a7ac7 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,8 @@
"dev2:saas": "pnpm build:entry && pnpm build:component-cssvar && pnpm -C examples/vue2 dev:saas",
"dev2.7": "pnpm build:entry && pnpm build:component-cssvar && pnpm -C examples/vue2.7 dev",
"dev2.7:saas": "pnpm build:entry && pnpm build:component-cssvar && pnpm -C examples/vue2.7 dev:saas",
+ "dev:docs": "pnpm -C examples/docs docs:dev",
+ "dev:react": "pnpm build:entry-react && pnpm -C examples/react-docs run dev",
"// ---------- 启动官网文档 ----------": "",
"dev:site": "pnpm build:entry && pnpm build:component-cssvar && pnpm -C examples/sites start",
"dev:open-site": "pnpm build:entry && pnpm build:component-cssvar && pnpm -C examples/sites start:open",
@@ -108,7 +110,15 @@
"// ---------- 手工构建发布指定组件 (Beta) ----------": "",
"preci:deployBeta": "pnpm clean:build",
"ci:deployBeta": "pnpm build:ui",
- "postci:deployBeta": "lerna publish from-package --yes --dist-tag beta"
+ "postci:deployBeta": "lerna publish from-package --yes --dist-tag beta",
+ "analyse:depends": "pnpm --filter @opentiny/analyse_depends start",
+ "build:entry-react": "pnpm -C internals/cli build:entry-react",
+ "create:mapping-react": "pnpm -C internals/cli create:mapping-react",
+ "build:react": "pnpm -C internals/cli build:react",
+ "build:ui-react": "pnpm create:mapping-react && pnpm build:entry-react && pnpm build:react",
+ "pub:react": "pnpm --filter=\"./packages/dist-react/**\" publish --no-git-checks --access=public",
+ "dev:react-site": "pnpm --filter @opentiny/react-site start",
+ "build:react-site": "pnpm --filter @opentiny/react-site build"
},
"dependencies": {
"@vue/composition-api": "1.2.2",
@@ -241,4 +251,4 @@
"> 1%",
"last 2 versions"
]
-}
+}
\ No newline at end of file
diff --git a/packages/modules-react.json b/packages/modules-react.json
new file mode 100644
index 000000000..846b83360
--- /dev/null
+++ b/packages/modules-react.json
@@ -0,0 +1,56 @@
+{
+ "Alert": {
+ "path": "react/src/alert/index.ts",
+ "type": "component",
+ "exclude": false,
+ "mode": [
+ "mobile-first",
+ "pc"
+ ]
+ },
+ "AlertMobileFirst": {
+ "path": "react/src/alert/src/mobile-first.jsx",
+ "type": "template",
+ "exclude": false
+ },
+ "AlertPc": {
+ "path": "react/src/alert/src/pc.jsx",
+ "type": "template",
+ "exclude": false
+ },
+ "Button": {
+ "path": "react/src/button/index.ts",
+ "type": "component",
+ "exclude": false,
+ "mode": [
+ "mobile-first",
+ "mobile",
+ "pc"
+ ]
+ },
+ "ButtonMobile": {
+ "path": "react/src/button/src/mobile.jsx",
+ "type": "template",
+ "exclude": false
+ },
+ "ButtonMobileFirst": {
+ "path": "react/src/button/src/mobile-first.jsx",
+ "type": "template",
+ "exclude": false
+ },
+ "ButtonPc": {
+ "path": "react/src/button/src/pc.jsx",
+ "type": "template",
+ "exclude": false
+ },
+ "Common": {
+ "path": "react-common/src/index.ts",
+ "type": "module",
+ "exclude": false
+ },
+ "Icon": {
+ "path": "react-icon/index.ts",
+ "type": "module",
+ "exclude": false
+ }
+}
diff --git a/packages/react-common/package.json b/packages/react-common/package.json
new file mode 100644
index 000000000..c3bf8374b
--- /dev/null
+++ b/packages/react-common/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@opentiny/react-common",
+ "version": "1.0.0",
+ "description": "",
+ "main": "src/index.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@opentiny/vue-renderless": "workspace:~",
+ "@opentiny/vue-theme": "workspace:~",
+ "classnames": "^2.3.2",
+ "react": "18.2.0",
+ "tailwind-merge": "^1.8.0"
+ }
+}
diff --git a/packages/react-common/src/csscls.ts b/packages/react-common/src/csscls.ts
new file mode 100644
index 000000000..27f274a6e
--- /dev/null
+++ b/packages/react-common/src/csscls.ts
@@ -0,0 +1,65 @@
+interface CssClassObject {
+ [k: string]: any
+}
+type CssClassArray = Array
+export type CssClass = string | CssClassObject | CssClassArray
+
+/**
+ * 简单合并 tailwind 类对象为字符串值
+ *
+ * @param cssClassObject tailwind 类对象
+ * @returns string
+ */
+const stringifyCssClassObject = (cssClassObject: CssClassObject): string => {
+ const allCssClass: Array = []
+
+ Object.keys(cssClassObject).forEach((cssClass) => cssClassObject[cssClass] && allCssClass.push(cssClass))
+
+ return allCssClass.join('\u{20}')
+}
+
+/**
+ * 简单合并 tailwind 类数组为字符串值
+ *
+ * @param cssClassArray tailwind 类数组
+ * @returns string
+ */
+const stringifyCssClassArray = (cssClassArray: CssClassArray): string => {
+ const allCssClass: Array = []
+
+ cssClassArray.forEach((cssClass) => {
+ if (typeof cssClass === 'string') {
+ allCssClass.push(cssClass)
+ } else if (typeof cssClass === 'object') {
+ allCssClass.push(stringifyCssClassObject(cssClass))
+ }
+ })
+
+ return allCssClass.join('\u{20}')
+}
+
+/**
+ * 简单合并 tailwind 类对象为字符串值,去重处理留给 tailwind-merge 处理
+ *
+ * @param {*} cssClasses tailwind 类集合
+ * @returns string
+ */
+export const stringifyCssClass = (cssClasses: Array): string => {
+ if (!cssClasses || (Array.isArray(cssClasses) && !cssClasses.length)) return ''
+
+ const allCssClass: Array = []
+
+ cssClasses.forEach((cssClass) => {
+ if (cssClass) {
+ if (typeof cssClass === 'string') {
+ allCssClass.push(cssClass)
+ } else if (Array.isArray(cssClass)) {
+ allCssClass.push(stringifyCssClassArray(cssClass))
+ } else if (typeof cssClass === 'object') {
+ allCssClass.push(stringifyCssClassObject(cssClass))
+ }
+ }
+ })
+
+ return allCssClass.join('\u{20}')
+}
diff --git a/packages/react-common/src/event.js b/packages/react-common/src/event.js
new file mode 100644
index 000000000..277ce307d
--- /dev/null
+++ b/packages/react-common/src/event.js
@@ -0,0 +1,95 @@
+import { eventBus } from './utils'
+
+const $busMap = new Map()
+
+export const emit = (props) => (evName, ...args) => {
+ const reactEvName = 'on'
+ + evName.substr(0, 1).toUpperCase()
+ + evName.substr(1)
+
+ if (props[reactEvName] && typeof props[reactEvName] === 'function') {
+ props[reactEvName](...args)
+ }
+ else {
+ const $bus = $busMap.get(props)
+ if ($bus) {
+ $bus.emit(evName, ...args)
+ }
+ }
+}
+export const on = (props) => (evName, callback) => {
+ if ($busMap.get(props)) {
+ const $bus = $busMap.get(props)
+ $bus.on(evName, callback)
+ }
+ else {
+ const $bus = eventBus()
+ $bus.on(evName, callback)
+ $busMap.set(props, $bus)
+ }
+}
+export const off = (props) => (evName, callback) => {
+ const $bus = $busMap.get(props)
+ if (!$bus) return
+ $bus.off(evName, callback)
+}
+export const once = (props) => (evName, callback) => {
+ let $bus = null
+ const onceCallback = (...args) => {
+ callback(...args)
+ $bus && $bus.off(evName, onceCallback)
+ }
+
+ if ($busMap.get(props)) {
+ $bus = $busMap.get(props)
+ $bus.on(evName, onceCallback)
+ }
+ else {
+ $bus = eventBus()
+ $bus.on(evName, onceCallback)
+ $busMap.set(props, $bus)
+ }
+}
+export const emitEvent = (vm) => {
+ const broadcast = (vm, componentName, eventName, ...args) => {
+ const children = vm.$children
+
+ Array.isArray(children)
+ && children.forEach((child) => {
+ const name = child.$options && child.$options.componentName
+ const component = child
+
+ if (name === componentName) {
+ component.emit(eventName, ...args)
+ // todo: 调研 component.$emitter
+ // component.$emitter && component.$emitter.emit(eventName, params)
+ }
+ else {
+ broadcast(child, componentName, eventName, ...args)
+ }
+ })
+ }
+
+ return {
+ dispatch(componentName, eventName, ...args) {
+ let parent = vm.$parent
+ if (parent.type === null) return
+ let name = parent.$options && parent.$options.componentName
+ while (parent && parent.type && (!name || name !== componentName)) {
+ parent = parent.$parent
+
+ if (parent)
+ name = parent.$options && parent.$options.componentName
+ }
+
+ if (parent) {
+ parent.emit(eventName, ...args)
+ // fix: VUE3下事件参数为数组,VUE2下事件参数不是数组,这里修改为和VUE2兼容
+ // parent.$emitter && parent.$emitter.emit(...[eventName].concat(params))
+ }
+ },
+ broadcast(componentName, eventName, ...args) {
+ broadcast(vm, componentName, eventName, ...args)
+ }
+ }
+}
diff --git a/packages/react-common/src/fiber.js b/packages/react-common/src/fiber.js
new file mode 100644
index 000000000..0d870530f
--- /dev/null
+++ b/packages/react-common/src/fiber.js
@@ -0,0 +1,121 @@
+import { useRef, useEffect, useState } from 'react'
+import { compWhiteList } from './virtual-comp.jsx'
+
+function getFiberByDom(dom) {
+ const key = Object.keys(dom).find((key) => {
+ return (
+ key.startsWith('__reactFiber$') || // react 17+
+ key.startsWith('__reactInternalInstance$')
+ ) // react <17
+ })
+
+ return dom[key]
+}
+
+function traverseFiber(fiber, breaker, handler) {
+ if (!fiber) return
+ typeof handler === 'function' && handler(fiber)
+ if (typeof breaker !== 'function') {
+ breaker = () => { }
+ }
+ traverseFiber(fiber.sibling, breaker, handler)
+ breaker(fiber) || traverseFiber(fiber.child, breaker, handler)
+}
+
+function collectFiber(fiber, collector, mapper) {
+ const collect = []
+ traverseFiber(
+ fiber,
+ ({ type }) => {
+ if (type && typeof type !== 'string') {
+ return !compWhiteList.includes(type.name)
+ }
+ return false
+ },
+ (fiber) => {
+ if (typeof collector === 'function' && collector(fiber)) {
+ collect.push(fiber)
+ }
+ }
+ )
+ return typeof mapper === 'function'
+ ? collect.map(mapper)
+ : collect
+}
+
+function collectChildren(fiber) {
+ return collectFiber(fiber, (fiber) => {
+ return fiber.type && typeof fiber.type !== 'string'
+ })
+}
+
+function collectRefs(fiber) {
+ return collectFiber(
+ fiber,
+ (fiber) => {
+ if (typeof fiber.type === 'string') {
+ return fiber.stateNode.getAttribute('v_ref')
+ }
+ else {
+ return fiber.memoizedProps.v_ref
+ }
+ },
+ (fiber) => {
+ if (typeof fiber.type === 'string') {
+ return {
+ [fiber.stateNode.getAttribute('v_ref')]: fiber.stateNode
+ }
+ }
+ else {
+ return {
+ [fiber.memoizedProps.v_ref]: fiber
+ }
+ }
+ }
+ ).reduce((pre, cur) => {
+ Object.assign(pre, cur)
+ return pre
+ }, {})
+}
+
+const parentMap = new WeakMap()
+export function getParentFiber(fiber, isFirst = true, child = fiber) {
+ if (!fiber || !fiber.return) return null
+ if (parentMap.has(child)) return parentMap.get(child)
+ if (fiber.type && typeof fiber.type !== 'string' && !isFirst) {
+ parentMap.set(child, fiber)
+ return fiber
+ }
+ return getParentFiber(fiber.return, false, fiber)
+}
+
+export function creatFiberCombine(fiber) {
+ return {
+ fiber,
+ refs: fiber && collectRefs(fiber.child),
+ children: fiber && collectChildren(fiber.child),
+ }
+}
+
+export function useFiber() {
+ const ref = useRef()
+ const [parent, setParent] = useState()
+ const [current, setCurrent] = useState()
+ useEffect(() => {
+ if (ref.current) {
+ const current_fiber = getFiberByDom(ref.current)
+ setParent(
+ getParentFiber(
+ current_fiber.return
+ )
+ )
+ setCurrent(current_fiber.return)
+ }
+ }, [])
+
+ return {
+ ref,
+ parent: creatFiberCombine(parent),
+ current: creatFiberCombine(current)
+ }
+}
diff --git a/packages/react-common/src/index.ts b/packages/react-common/src/index.ts
new file mode 100644
index 000000000..9913960e3
--- /dev/null
+++ b/packages/react-common/src/index.ts
@@ -0,0 +1,111 @@
+import * as hooks from 'react'
+import { Svg } from './svg-render.jsx'
+
+import { nextTick, ref, computed, readonly, watch, onBeforeUnmount, inject } from './vue-hooks.js'
+import { emit, on, off, once, emitEvent } from './event.js'
+import { If, Component, Slot, For, Transition } from './virtual-comp.jsx'
+import { filterAttrs, vc, getElementCssClass } from './utils.js'
+import { useFiber } from './fiber.js'
+import { useVm } from './vm.js'
+import { useReactive } from './reactive.js'
+import { twMerge } from 'tailwind-merge'
+import { stringifyCssClass } from './csscls.js'
+
+import '@opentiny/vue-theme/base/index.less'
+
+const vue_hooks = {
+ nextTick,
+ ref,
+ computed,
+ readonly,
+ watch,
+ onBeforeUnmount,
+ inject
+}
+
+// emitEvent, dispath, broadcast
+export const $prefix = 'Tiny'
+
+export const mergeClass = (...cssClasses) => twMerge(stringifyCssClass(cssClasses))
+
+export const useSetup = ({
+ props,
+ // context,
+ renderless,
+ api,
+ extendOptions = {},
+ // mono = false,
+ classes = {},
+ constants,
+ vm,
+ parent
+}) => {
+ const render = typeof props.tiny_renderless === 'function' ? props.tiny_renderless : renderless
+ const { dispath, broadcast } = emitEvent(vm)
+
+ const utils = {
+ vm,
+ parent,
+ emit: emit(props),
+ constants,
+ nextTick,
+ dispath,
+ broadcast,
+ t() { },
+ mergeClass
+ }
+ const sdk = render(
+ props,
+ {
+ ...hooks,
+ useReactive,
+ ...vue_hooks,
+ reactive: useReactive
+ },
+ utils,
+ extendOptions
+ )
+ const attrs = {
+ a: filterAttrs,
+ m: mergeClass,
+ vm: utils.vm,
+ gcls: (key) => getElementCssClass(classes, key),
+ }
+
+ if (Array.isArray(api)) {
+ api.forEach((name) => {
+ const value = sdk[name]
+
+ if (typeof value !== 'undefined') {
+ attrs[name] = value
+ }
+ })
+ }
+
+ return attrs
+}
+
+export {
+ Svg,
+ vc,
+ If,
+ Component,
+ Slot,
+ For,
+ Transition,
+ emit,
+ on,
+ off,
+ once,
+ emitEvent,
+ useVm,
+ nextTick,
+ useFiber,
+ ref,
+ computed,
+ readonly,
+ useReactive,
+ watch
+}
+
+export const reactive = useReactive
diff --git a/packages/react-common/src/reactive.js b/packages/react-common/src/reactive.js
new file mode 100644
index 000000000..42ca5371e
--- /dev/null
+++ b/packages/react-common/src/reactive.js
@@ -0,0 +1,104 @@
+import { useState, useRef } from 'react'
+import { computed } from './vue-hooks'
+
+// 响应式核心
+const reactiveMap = new WeakMap()
+const reactive = (
+ staticObject,
+ handler = {},
+ path = [],
+ rootStaticObject = staticObject
+) => {
+ reactiveMap.has(staticObject) || reactiveMap.set(staticObject, new Proxy(staticObject, {
+ get(
+ target,
+ property,
+ receiver
+ ) {
+ const targetVal = target[property]
+ if (targetVal && targetVal['v-hooks-type'] === computed) {
+ return targetVal.value
+ }
+
+ const _path = [...path, property]
+ const res = typeof targetVal === 'object'
+ ? reactive(targetVal, handler, _path, rootStaticObject)
+ : targetVal
+
+ // 监听访问
+ handler.get && handler.get({
+ result: res,
+ root: rootStaticObject,
+ path: _path,
+ target,
+ property,
+ receiver
+ })
+
+ return res
+ },
+ set(
+ target,
+ property,
+ value,
+ receiver
+ ) {
+ const targetVal = target[property]
+ if (targetVal && targetVal['v-hooks-type'] === computed) {
+ targetVal.value = value
+ return true
+ }
+
+ const _path = [...path, property]
+
+ // 监听修改
+ handler.set && handler.set({
+ target,
+ property,
+ receiver,
+ root: rootStaticObject,
+ path: _path,
+ newVal: value,
+ oldVal: target[property]
+ })
+
+ target[property] = value
+ return true
+ }
+ }))
+
+ return reactiveMap.get(staticObject)
+}
+
+export const useReload = () => {
+ const setReload = useState(0)[1]
+ return () => setReload(pre => pre + 1)
+}
+
+const isObject = (val) => val !== null && typeof val === 'object'
+
+// 用于从 hooks 链表中查找 reactive 生成的 state
+export function Reactive(obj) {
+ Object.keys(obj).forEach(key => {
+ this[key] = obj[key]
+ })
+}
+
+export const useReactive = (initalObject) => {
+ if (!isObject(initalObject)) {
+ return initalObject
+ }
+ const memoried = useRef()
+ const proxy = useRef()
+ const reload = useReload()
+
+ if (!memoried.current && !proxy.current) {
+ memoried.current = new Reactive(initalObject)
+ proxy.current = reactive(memoried.current, {
+ set() {
+ reload()
+ }
+ })
+ }
+ return proxy.current
+}
diff --git a/packages/react-common/src/resolve-props.js b/packages/react-common/src/resolve-props.js
new file mode 100644
index 000000000..b44730eb8
--- /dev/null
+++ b/packages/react-common/src/resolve-props.js
@@ -0,0 +1,35 @@
+// todo: 一个方法去拿到 props 身上的事件,以 on 为前缀
+const reactEventPrefix = /^on[A-Z]/
+export function getEventByReactProps(props) {
+ const $listeners = {}
+ Object
+ .keys(props)
+ .filter(propName => {
+ return reactEventPrefix.test(propName)
+ && typeof props[propName] === 'function'
+ })
+ .map(reactEvName => {
+ return {
+ reactEvName,
+ vueEvName: reactEvName.substr(2).toLowerCase()
+ }
+ })
+ .forEach(({ reactEvName, vueEvName }) => {
+ Object.assign($listeners, {
+ [vueEvName]: props[reactEvName]
+ })
+ })
+ return $listeners
+}
+export function getAttrsByReactProps(props) {
+ const $attrs = {}
+ Object
+ .keys(props)
+ .filter(propName => {
+ return !reactEventPrefix.test(propName) && !['children'].includes(propName)
+ })
+ .forEach((attr) => {
+ $attrs[attr] = props[attr]
+ })
+ return $attrs
+}
diff --git a/packages/react-common/src/svg-render.jsx b/packages/react-common/src/svg-render.jsx
new file mode 100644
index 000000000..030d657d7
--- /dev/null
+++ b/packages/react-common/src/svg-render.jsx
@@ -0,0 +1,23 @@
+import classNames from 'classnames'
+import { If } from './virtual-comp.jsx'
+
+export const Svg = ({ name = 'Icon', component: Icon }) => {
+ const funcObj = ({
+ [name](props) {
+ const className = classNames(
+ 'icon',
+ 'tiny-svg',
+ props.className
+ )
+ const v_if = typeof props['v-if'] === 'boolean' ? props['v-if'] : true
+ const defaultProps = { ...props }
+ delete defaultProps['v-if']
+ return (
+
+
+
+ )
+ }
+ })
+ return funcObj[name]
+}
diff --git a/packages/react-common/src/utils.ts b/packages/react-common/src/utils.ts
new file mode 100644
index 000000000..b7ee7c535
--- /dev/null
+++ b/packages/react-common/src/utils.ts
@@ -0,0 +1,110 @@
+/**
+ * filterAttrs 属性过滤函数
+ * @param {object} attrs 由父组件传入,且没有被子组件声明为 props 的一些属性
+ * @param {Array} filters 过滤数组,元素可以为字符串,也可以为正则表达式
+ * @param {boolean} include 是否返回为被过滤的属性集合,如果为 false,filters 是过滤不要的属性
+ * @returns {object} 过滤后的属性对象
+ */
+export const filterAttrs = (attrs, filters, include) => {
+ const props = {}
+
+ for (let name in attrs) {
+ const find = filters.some((r) => new RegExp(r).test(name))
+
+ if ((include && find) || (!include && !find)) {
+ props[name] = attrs[name]
+ }
+ }
+
+ return props
+}
+
+/**
+ * event bus
+ * $bus.on
+ * $bus.off
+ * $bus.emit
+ */
+
+export const eventBus = () => {
+ const $bus = {}
+
+ const on = (eventName, callback) => {
+ if (!$bus[eventName]) {
+ $bus[eventName] = []
+ }
+
+ $bus[eventName].push(callback)
+ }
+
+ const off = (eventName, callback) => {
+ if (!$bus[eventName]) {
+ return
+ }
+
+ $bus[eventName] = $bus[eventName].filter(
+ subscriber => subscriber !== callback
+ )
+ }
+
+ const emit = (eventName, ...args) => {
+ if (!$bus[eventName]) {
+ return
+ }
+
+ $bus[eventName].forEach(subscriber => subscriber(...args))
+ }
+
+ return {
+ on,
+ emit,
+ off
+ }
+}
+
+/**
+ * 实现 vue 中 :class 的用法
+ */
+
+export function VueClassName(className) {
+ if (typeof className === 'string') {
+ return className
+ }
+ else if (Array.isArray(className)) {
+ return className.reduce((pre, cur, index) => {
+ if (typeof cur === 'string') {
+ return `${pre}${index === 0 ? '' : ' '}${cur}`
+ }
+ else {
+ return `${pre}${index === 0 ? '' : ' '}${VueClassName(cur)}`
+ }
+ }, '')
+ }
+ else if (typeof className === 'object') {
+ return Object
+ .keys(className)
+ .reduce((pre, key, index) => {
+ if (className[key]) {
+ return `${pre}${index === 0 ? '' : ' '}${key}`
+ }
+ else {
+ return pre
+ }
+ }, '')
+ }
+}
+
+export const vc = VueClassName
+
+export const getElementCssClass = (classes = {}, key) => {
+ if (typeof key === 'object') {
+ const keys = Object.keys(key)
+ let cls = ''
+ keys.forEach((k) => {
+ if (key[k] && classes[k]) cls += `${classes[k]} `
+ })
+ return cls
+ } else {
+ return classes[key] || ''
+ }
+}
diff --git a/packages/react-common/src/virtual-comp.jsx b/packages/react-common/src/virtual-comp.jsx
new file mode 100644
index 000000000..9db48a105
--- /dev/null
+++ b/packages/react-common/src/virtual-comp.jsx
@@ -0,0 +1,72 @@
+export function If(props) {
+ if (props['v-if']) {
+ return (props.children)
+ }
+ else {
+ return ''
+ }
+}
+
+export function Component(props) {
+ const Is = props.is || (() => '')
+ return
+
+
+}
+
+export function Slot(props) {
+ const {
+ name = 'default',
+ slots = {},
+ parent_children
+ } = props
+
+ const EmptySlot = () => '';
+
+ const S = slots[name] || EmptySlot
+
+ return (<>
+
+ {parent_children || props.children}
+
+
+
+
+
+
+ {props.children}
+
+
+ >)
+}
+
+export function For(props) {
+ const {
+ item: Item,
+ list = []
+ } = props
+
+ const listItems = list.map((item, index, list) => {
+ return ( )
+ })
+
+ return (<>{listItems}>)
+}
+
+export function Transition(props) {
+ const {
+ name
+ } = props
+
+ // 在 useEffect 里监听 dom 变化,拿到变化的 dom,给上面添加样式
+
+ return <>{props.children}>
+}
+
+export const compWhiteList = [
+ 'If',
+ 'Component',
+ 'Slot',
+ 'For',
+ 'Transition'
+]
diff --git a/packages/react-common/src/vm.js b/packages/react-common/src/vm.js
new file mode 100644
index 000000000..3a6cd9e7c
--- /dev/null
+++ b/packages/react-common/src/vm.js
@@ -0,0 +1,127 @@
+import { useFiber, getParentFiber, creatFiberCombine } from './fiber.js'
+import {
+ getEventByReactProps,
+ getAttrsByReactProps
+} from './resolve-props.js'
+import { Reactive } from './reactive.js'
+import { emit, on, off, once } from './event.js'
+
+const vmProxy = {
+ $parent: ({ fiber }) => {
+ const parentFiber = getParentFiber(fiber)
+ if (!parentFiber) return null
+ return createVmProxy(
+ creatFiberCombine(
+ parentFiber
+ )
+ )
+ },
+ $el: ({ fiber }) => fiber.child?.stateNode,
+ $refs: ({ refs, fiber }) => createRefsProxy(refs, fiber.constructor),
+ $children: ({ children }) => children.map((fiber) => createVmProxy(
+ creatFiberCombine(
+ getParentFiber(fiber)
+ )
+ )),
+ $listeners: ({ fiber }) => getEventByReactProps(fiber.memoizedProps),
+ $attrs: ({ fiber }) => getAttrsByReactProps(fiber.memoizedProps),
+ $slots: ({ fiber }) => fiber.memoizedProps.slots,
+ $scopedSlots: ({ fiber }) => fiber.memoizedProps.slots,
+ $options: ({ fiber }) => ({ componentName: fiber.type.name }),
+ $constants: ({ fiber }) => fiber.memoizedProps._constants,
+ $template: ({ fiber }) => fiber.memoizedProps.tiny_template,
+ $renderless: ({ fiber }) => fiber.memoizedProps.tiny_renderless,
+ $mode: () => 'pc',
+ state: ({ fiber }) => findStateInHooks(fiber.memoizedState),
+ $type: ({ fiber }) => fiber.type,
+ $service: (_, vm) => vm.state?.$service,
+ $emit: ({ fiber }) => emit(fiber.memoizedProps),
+ $on: ({ fiber }) => on(fiber.memoizedProps),
+ $once: ({ fiber }) => once(fiber.memoizedProps),
+ $off: ({ fiber }) => off(fiber.memoizedProps),
+ $set: () => (target, propName, value) => target[propName] = value
+}
+
+const vmProxyMap = new WeakMap()
+function createVmProxy(fiberCombine) {
+ if (!vmProxyMap.has(fiberCombine)) {
+ vmProxyMap.set(fiberCombine, new Proxy(fiberCombine, {
+ get(
+ target,
+ property,
+ receiver
+ ) {
+ if (!vmProxy[property]) {
+ return target.fiber.memoizedProps[property]
+ }
+ return vmProxy[property](target, receiver)
+ },
+ set(
+ target,
+ property,
+ value
+ ) {
+ return true
+ }
+ }))
+ }
+ return vmProxyMap.get(fiberCombine)
+}
+
+function createEmptyProxy() {
+ return new Proxy({}, {
+ get() {
+ return undefined
+ },
+ set() {
+ return true
+ }
+ })
+}
+
+function createRefsProxy(refs, FiberNode) {
+ return new Proxy(refs, {
+ get(
+ target,
+ property
+ ) {
+ if (target[property] instanceof FiberNode) {
+ return createVmProxy(
+ creatFiberCombine(target[property])
+ )
+ }
+ else {
+ return target[property]
+ }
+ }
+ })
+}
+
+function findStateInHooks(hookStart) {
+ let curHook = hookStart
+ // find state from hooks chain by Constructor Reactive
+ while (curHook) {
+ const refCurrent = curHook.memoizedState && curHook.memoizedState.current
+ if (refCurrent instanceof Reactive) break
+ curHook = curHook.next
+ }
+ return curHook
+ && curHook.memoizedState
+ && curHook.memoizedState.current
+}
+
+export function useVm() {
+ const { ref, current, parent } = useFiber()
+ if (!ref.current) {
+ return {
+ ref,
+ current: createEmptyProxy(),
+ parent: createEmptyProxy()
+ }
+ }
+ return {
+ ref,
+ current: current.fiber && createVmProxy(current),
+ parent: parent.fiber && createVmProxy(parent)
+ }
+}
diff --git a/packages/react-common/src/vue-hooks.js b/packages/react-common/src/vue-hooks.js
new file mode 100644
index 000000000..ec4bbb255
--- /dev/null
+++ b/packages/react-common/src/vue-hooks.js
@@ -0,0 +1,199 @@
+import { useRef, useEffect } from 'react'
+import { useReload } from './reactive'
+
+function Create(target) {
+ Object.keys(target).forEach(key => {
+ this[key] = target[key]
+ })
+}
+function Readonly(target) {
+ Create.call(this, target)
+}
+
+const useDepChange = (dependencies, immediate = false) => {
+ let isDepChange = false
+ const pre_dep = useRef()
+ if (!pre_dep.current) {
+ isDepChange = true && immediate
+ }
+ else {
+ for (let i in dependencies) {
+ if (pre_dep.current[i] !== dependencies[i]) {
+ isDepChange = true
+ break
+ }
+ }
+ }
+ pre_dep.current = dependencies
+ return isDepChange
+}
+
+export const nextTick = (callback) => {
+ queueMicrotask(callback)
+}
+
+export const ref = (value) => {
+ const reload = useReload()
+ const proxy = useRef()
+ if (!proxy.current) {
+ proxy.current = new Proxy({
+ value
+ }, {
+ get(target, property) {
+ if (property !== 'value') return
+ return target[property]
+ },
+ set(target, property, newVal) {
+ if (property !== 'value') return true
+ target[property] = newVal
+ reload()
+ return true
+ }
+ })
+ }
+ return proxy.current
+}
+
+export function computed(getter) {
+ const thisObj = {}
+ Object.setPrototypeOf(thisObj, computed.prototype)
+ if (typeof getter === 'function') {
+ thisObj.get = getter
+ }
+ else if (typeof getter === 'object') {
+ if (typeof getter.get === 'function') {
+ thisObj.get = getter.get
+ }
+ if (typeof getter.set === 'function') {
+ thisObj.set = getter.set
+ }
+ }
+ return new Proxy({
+ value: ''
+ }, {
+ get(
+ target,
+ property,
+ receiver
+ ) {
+ if (property === 'v-hooks-type') {
+ return computed
+ }
+ else if (property === 'value') {
+ return thisObj.get()
+ }
+ },
+ set(
+ target,
+ property,
+ value,
+ receiver
+ ) {
+ if (property === 'value') {
+ if (typeof thisObj.set === 'function') {
+ thisObj.set(value)
+ }
+ return true
+ }
+ return true
+ }
+ })
+}
+
+export const readonly = (target) => {
+ const proxy = useRef()
+ if (!proxy.current) {
+ proxy.current = new Proxy(new Readonly(target), {
+ get: (target, property) => target[property],
+ set: () => true
+ })
+ }
+ return proxy.current
+}
+
+export const watchEffect = (effect, dependencies, options) => {
+ const cache = useRef()
+ const isDepChange = useDepChange(dependencies)
+ if (!cache.current) cache.current = { effect: true }
+ const { flush } = options || { flust: 'pre' }
+ const onCleanUp = (callback) => cache.current.clean = callback
+ if (cache.current.effect && isDepChange) {
+ const clean = cache.current.clean
+ typeof clean === 'function' && clean()
+ if (flush === 'pre') {
+ effect(onCleanUp)
+ }
+ else if (flush === 'sync') {
+ effect(onCleanUp)
+ }
+ else {
+ nextTick(() => {
+ effect(onCleanUp)
+ })
+ }
+ }
+ return () => cache.current.effect = false
+}
+
+export const watchPostEffect = (effect, dependencies) => watchEffect(effect, dependencies, { flush: 'post' })
+
+export const watch = (source, callback, options = {}) => {
+ const cache = useRef()
+ let source_value
+ if (Array.isArray(source)) {
+ source_value = source.map((item) => typeof item === 'function' && item())
+ }
+ else {
+ source_value = [(typeof source === 'function' && source())]
+ }
+ const isDepChange = useDepChange(source_value, options.immediate)
+ if (!cache.current) cache.current = { clear: false }
+ if (isDepChange && !cache.current.clear) {
+ callback(
+ source_value.length === 1 ? source_value[0] : source_value,
+ cache.current.pre
+ )
+ }
+ cache.current.pre = source_value
+ return () => cache.current.clear = true
+}
+
+const provideMap = new WeakMap()
+export const provide = (vm) => (key, value) => {
+ if (!provideMap.has(vm)) {
+ provideMap.set(vm, {})
+ }
+ const provideObj = provideMap.get(vm)
+ provideObj[key] = value
+}
+
+export const inject = (_parent) => (key, defaultValue, treatDefaultAsFactory) => {
+ let parent = _parent
+ let context = null
+
+ while (parent) {
+ parent = parent.$parent
+ if (provideMap.has(parent)) {
+ context = provideMap.get(parent)
+ break
+ }
+ }
+
+ let val = context && context[key]
+ if (!val) {
+ val = treatDefaultAsFactory ? defaultValue() : defaultValue
+ }
+ return val
+}
+
+export const onBeforeUnmount = (callback) => {
+ useEffect(() => {
+ return callback
+ }, [])
+}
+
+export const onMounted = (callback) => {
+ useEffect(() => {
+ callback()
+ }, [])
+}
diff --git a/packages/react-icon/index.ts b/packages/react-icon/index.ts
new file mode 100644
index 000000000..a31cf4e73
--- /dev/null
+++ b/packages/react-icon/index.ts
@@ -0,0 +1,39 @@
+import IconLoading from './src/loading'
+import IconAdd from './src/add'
+import IconCheck from './src/check'
+import IconCheckedSur from './src/check'
+import IconChevronDown from './src/chevron-down'
+import IconClose from './src/close'
+import IconError from './src/error'
+import IconHalfSelect from './src/half-select'
+import IconHelp from './src/help'
+import IconSuccess from './src/success'
+import IconWarning from './src/warning'
+
+export {
+ IconLoading,
+ IconAdd,
+ IconCheck,
+ IconCheckedSur,
+ IconChevronDown,
+ IconClose,
+ IconError,
+ IconHalfSelect,
+ IconHelp,
+ IconSuccess,
+ IconWarning
+}
+
+export default {
+ IconLoading,
+ IconAdd,
+ IconCheck,
+ IconCheckedSur,
+ IconChevronDown,
+ IconClose,
+ IconError,
+ IconHalfSelect,
+ IconHelp,
+ IconSuccess,
+ IconWarning
+}
diff --git a/packages/react-icon/package.json b/packages/react-icon/package.json
new file mode 100644
index 000000000..f3d799d2e
--- /dev/null
+++ b/packages/react-icon/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "@opentiny/react-icon",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@opentiny/react-common": "workspace:~",
+ "@opentiny/vue-theme": "workspace:~"
+ }
+}
diff --git a/packages/react-icon/src/add/index.ts b/packages/react-icon/src/add/index.ts
new file mode 100644
index 000000000..4baf741e0
--- /dev/null
+++ b/packages/react-icon/src/add/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as AddLoading } from '@opentiny/vue-theme/svgs/add.svg'
+
+export default Svg({ name: 'AddLoading', component: AddLoading })
diff --git a/packages/react-icon/src/check/index.ts b/packages/react-icon/src/check/index.ts
new file mode 100644
index 000000000..730745299
--- /dev/null
+++ b/packages/react-icon/src/check/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Check } from '@opentiny/vue-theme/svgs/check.svg'
+
+export default Svg({ name: 'Check', component: Check })
diff --git a/packages/react-icon/src/checked-sur/index.ts b/packages/react-icon/src/checked-sur/index.ts
new file mode 100644
index 000000000..5080c7240
--- /dev/null
+++ b/packages/react-icon/src/checked-sur/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as CheckedSur } from '@opentiny/vue-theme/svgs/checked-sur.svg'
+
+export default Svg({ name: 'CheckedSur', component: CheckedSur })
diff --git a/packages/react-icon/src/chevron-down/index.ts b/packages/react-icon/src/chevron-down/index.ts
new file mode 100644
index 000000000..10378f131
--- /dev/null
+++ b/packages/react-icon/src/chevron-down/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as ChevronDown } from '@opentiny/vue-theme/svgs/chevron-down.svg'
+
+export default Svg({ name: 'ChevronDown', component: ChevronDown })
diff --git a/packages/react-icon/src/close/index.ts b/packages/react-icon/src/close/index.ts
new file mode 100644
index 000000000..ea5aae990
--- /dev/null
+++ b/packages/react-icon/src/close/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Close } from '@opentiny/vue-theme/svgs/close.svg'
+
+export default Svg({ name: 'Close', component: Close })
diff --git a/packages/react-icon/src/error/index.ts b/packages/react-icon/src/error/index.ts
new file mode 100644
index 000000000..93a519a20
--- /dev/null
+++ b/packages/react-icon/src/error/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Error } from '@opentiny/vue-theme/svgs/error.svg'
+
+export default Svg({ name: 'Error', component: Error })
diff --git a/packages/react-icon/src/half-select/index.ts b/packages/react-icon/src/half-select/index.ts
new file mode 100644
index 000000000..e95220b3a
--- /dev/null
+++ b/packages/react-icon/src/half-select/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as HalfSelect } from '@opentiny/vue-theme/svgs/halfselect.svg'
+
+export default Svg({ name: 'HalfSelect', component: HalfSelect })
diff --git a/packages/react-icon/src/help/index.ts b/packages/react-icon/src/help/index.ts
new file mode 100644
index 000000000..aff6c1fe4
--- /dev/null
+++ b/packages/react-icon/src/help/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Help } from '@opentiny/vue-theme/svgs/help.svg'
+
+export default Svg({ name: 'Help', component: Help })
diff --git a/packages/react-icon/src/loading/index.ts b/packages/react-icon/src/loading/index.ts
new file mode 100644
index 000000000..d0c5098b7
--- /dev/null
+++ b/packages/react-icon/src/loading/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as IconLoading } from '@opentiny/vue-theme/svgs/loading.svg'
+
+export default Svg({ name: 'IconLoading', component: IconLoading })
diff --git a/packages/react-icon/src/success/index.ts b/packages/react-icon/src/success/index.ts
new file mode 100644
index 000000000..c4a945975
--- /dev/null
+++ b/packages/react-icon/src/success/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Success } from '@opentiny/vue-theme/svgs/success.svg'
+
+export default Svg({ name: 'Success', component: Success })
diff --git a/packages/react-icon/src/warning/index.ts b/packages/react-icon/src/warning/index.ts
new file mode 100644
index 000000000..24e4ef591
--- /dev/null
+++ b/packages/react-icon/src/warning/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2022 - present TinyVue Authors.
+ * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Warning } from '@opentiny/vue-theme/svgs/warning.svg'
+
+export default Svg({ name: 'Warning', component: Warning })
diff --git a/packages/react/.depcheckrc.yaml b/packages/react/.depcheckrc.yaml
new file mode 100644
index 000000000..2a795f308
--- /dev/null
+++ b/packages/react/.depcheckrc.yaml
@@ -0,0 +1,2 @@
+ignores:
+ - "@opentiny/react*"
\ No newline at end of file
diff --git a/packages/react/mobile-first.ts b/packages/react/mobile-first.ts
new file mode 100644
index 000000000..ac4f830e1
--- /dev/null
+++ b/packages/react/mobile-first.ts
@@ -0,0 +1,14 @@
+import Alert from '@opentiny/react-alert/src/mobile-first'
+import Button from '@opentiny/react-button/src/mobile-first'
+import { $prefix } from '@opentiny/react-common'
+
+const components = [Alert, Button]
+
+export const version = '1.0.0'
+
+export { Alert, Button }
+
+export default {
+ Alert,
+ Button
+} as any
diff --git a/packages/react/package.json b/packages/react/package.json
new file mode 100644
index 000000000..08431a6e4
--- /dev/null
+++ b/packages/react/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@opentiny/react",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@opentiny/react-common": "workspace:~",
+ "@opentiny/react-alert": "workspace:~",
+ "@opentiny/react-button": "workspace:~"
+ }
+}
\ No newline at end of file
diff --git a/packages/react/readme.md b/packages/react/readme.md
new file mode 100644
index 000000000..360cf7fbe
--- /dev/null
+++ b/packages/react/readme.md
@@ -0,0 +1,313 @@
+# 开发文档
+
+## 下载依赖
+
+```bash
+pnpm i
+```
+
+## 生成 react 组件入口
+
+```bash
+pnpm build:entry-react
+```
+
+## 本地启动 react 调试项目
+
+```bash
+pnpm dev:react
+```
+
+## 本地启动 react 文档项目
+
+```bash
+pnpm dev:react-site
+```
+
+## 打包 react 组件
+
+```bash
+pnpm build:ui-react
+```
+
+运行此命令后,会在 pacakges-react 产生打包产物
+一般是
+ packages/dist-react/@opention/button
+ ... 单个组件产物
+ packages/dist-react/@opention/react-common
+ packages/dist-react/@opention/react
+ packages/dist-react/@opention/react-icon
+
+命令参数:传入字符串参数列表可以指定只打包单个组件或多个特定组件,比如
+
+```bash
+pnpm build:ui-react button
+```
+
+默认不传的话,会打包所有组件,以及公共任务,比如 react-common、react-icon
+
+可以通过 -f 指定目标格式,默认 es,可选 es、cjs
+可以通过 -t 指定目标版本,默认 18,现在 react 只支持 18
+可以通过 -s 指定发布 npm scope,默认是 opentiny
+可以通过 -c 指定是否清空构建目录
+可以通过 --no-dts 指定不生成类型定义文件
+
+## 发包 react 组件
+
+```bash
+pnpm pub:react
+```
+
+# 目录结构
+
+## 打包 react 相关
+
+```b
+internals/cli
+ /build
+ /build-entry-react.ts (packages/react 目录下生成入口)
+ /build-ui-react.ts (packages/dist-react 下生成打包产物)
+ /create
+ /create-mapping-react.ts (packages 下生成构建任务列表 modules.json)
+ /common-mapping-react.json (定义一些公共的打包任务,如 react-common)
+```
+
+## 开发 react 模版文件相关
+
+packages/react/src/[compName] 目录
+
+一个组件模版的目录结构如下
+
+```b
+alert
+ /node_modules
+ /src
+ /index.ts
+ /pc.tsx
+ /mobile.tsx
+ /mobile-first.ts
+ /index.ts
+ /package.json
+```
+alert/index 是组件入口
+pc、mobile、mobile-first 是三套模版
+
+## 开发 react-icon 相关
+
+packages/react-icon/src/[svgName] 目录
+
+一个 svg 直接用一个 index.ts 创建
+
+如:packages/react-icon/src/add/index.ts
+
+```ts
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as AddLoading } from '@opentiny/vue-theme/svgs/add.svg'
+
+export default Svg({ name: 'AddLoading', component: AddLoading })
+```
+
+## 开发 react-common react 适配层相关
+
+react-common 的目录如下,主要是适配层的文件
+
+```b
+packages/react-common
+ /src
+ /csscls.ts 操作样式类名的一些方法
+ /event.ts 模拟 vue 事件系统
+ /fiber.ts 对 fiber 的一些读取操作
+ /reactive.ts 实现数据响应式
+ /resolveProps.js 从 react 的 props 上解析事件或属性
+ /svg-render.jsx 渲染 svg 组建的公共函数
+ /utils.ts 工具函数
+ /virtual-comp.jsx 虚拟组件,用于实现 vue 的指令系统
+ /vm.js 用户模拟 vue 的 vm 对象
+ /vue-hooks.js 用户模拟 vue 的钩子函数
+```
+
+# 如何贡献一个 react 组建
+
+对于 vue 已有的组件,全新的组件,建议先实现 vue 组件,再去适配 react 模版。
+因为 vue 和 react 共用同一套 renderless 目录底下的逻辑,renderless 中的逻辑,vue 侧是基于 vue 自身的,react 侧则是基于 react 模拟 vue api 的适配层的。所以,先用 vue 的思维实现 vue 组件,是比较可靠的。
+
+## 开发
+
+假如要实现的组件是 button
+1. 在 packages/react/src 底下创建如下目录
+
+```js
+button
+ /node_modules
+ /src
+ /index.ts
+ /pc.tsx
+ /mobile.tsx
+ /mobile-first.ts
+ /index.ts
+ /package.json
+```
+
+2. 给 package.json 中添加相关依赖
+
+```json
+ "dependencies": {
+ "@opentiny/vue-renderless": "workspace:~", // 必选
+ "@opentiny/react-common": "workspace:~", // 必选
+ "@opentiny/react-icon": "workspace:~", // 组件需用用 icon,从这里引入
+ "@opentiny/vue-theme": "workspace:~" // 必须,组件的样式一般在这里定义
+ }
+```
+
+3. 创建 pc.tsx/mobile.tsx/mobile-first.tsx,并实现组件模版
+
+```tsx
+import { renderless, api } from '@opentiny/vue-renderless/button/vue'
+import { useSetup } from '@opentiny/react-common'
+import '@opentiny/vue-theme/button/index.less'
+
+export default function Button(props) {
+ const {
+ circle, // 解析出一个属性,api 事先定义的
+ type = 'default', // 解析出一个属性,有默认值
+ } = props
+
+ const defaultProps = Object.assign({
+ type
+ }, props) // 合并默认值属性
+
+ const {
+ ref,
+ parent,
+ current: vm
+ } = useVm() // 生成 vm 和 parent 对象,给 useSetup 提供 vm、parent 对象
+ // ref 是用来获取组件根部 dom ,进而获取 fiber 节点
+
+ const {
+ handleClick, // 从 renderless/**/button/index.ts 里定义的事件,会在 button/vue.ts
+ state,
+ a
+ } = useSetup({
+ props: defaultProps,
+ renderless,
+ api,
+ vm,
+ parent
+ }) // 通过 useSetup 函数抹平差异并执行组件核心逻辑 renderless 函数
+ // renderless 函数是通过 vue api 实现的组件逻辑
+ // useSetup 中给 renderless 函数提供了模拟后的 api
+
+ const $attrs = a(props, define_props, false)
+ // 过滤没有在组件上定义,但是传入的属性
+
+ return (
+
+ {state.data} // 在模版中使用状态数据
+
+ )
+}
+
+```
+
+4. 创建多模版模式组建
+
+button/src/index.ts
+```jsx
+import pc from './pc.jsx'
+import mobile from './mobile.jsx'
+import mobileFirst from './mobile-first.jsx'
+
+export default function (props) {
+ const {
+ tiny_mode = 'pc'
+ } = props
+
+ const S = ({
+ pc,
+ mobile,
+ 'mobile-first': mobileFirst
+ })[tiny_mode]
+
+ return (S(props))
+}
+```
+
+5. 创建组建入口
+button/index.ts
+```jsx
+import Button from './src'
+
+export default Button
+```
+
+## 开发一个 react-icon 组件
+
+在 packages/react-icon/src 下创建 iconName 目录
+中创建 index.ts 组件,内容如下:
+
+```ts
+import { Svg } from '@opentiny/react-common'
+import { ReactComponent as Check } from '@opentiny/vue-theme/svgs/check.svg'
+
+export default Svg({ name: 'Check', component: Check })
+```
+
+# 用户使用文档
+
+## 在项目中使用所有组件
+
+- 1.下载整个组建库
+```bash
+npm i @pe-3/react
+```
+
+- 2. 导入组件
+```js
+import { Button as TinyButton, React as Tiny React } from '@pe-3/react'
+```
+
+- 3. 使用组件(查看 api 文档)
+```js
+function App() {
+ return (
+ 主要按钮
+
+
)
+}
+```
+
+## 在项目中使用单个组建
+
+- 1. 下载单个组件
+
+```bash
+npm i @pe-3/react-button
+npm i @pe-3/react-alert
+```
+
+- 2. 导入单个组件
+```js
+import TintButton from '@pe-3/react-button'
+import TintAlert from '@pe-3/react-alert'
+```
+
+- 3. 使用单个组件
+```js
+function App() {
+ return (
+ 主要按钮
+
+
)
+}
+```
+
+## 组建 api 文档地址:
+
+https://opentiny.design/
+
+## codesandbox
+
+https://codesandbox.io/s/hungry-bash-tlch6l?file=/src/App.js
\ No newline at end of file
diff --git a/packages/react/src/alert/index.ts b/packages/react/src/alert/index.ts
new file mode 100644
index 000000000..9bc345130
--- /dev/null
+++ b/packages/react/src/alert/index.ts
@@ -0,0 +1,3 @@
+import Alert from './src'
+
+export default Alert
diff --git a/packages/react/src/alert/package.json b/packages/react/src/alert/package.json
new file mode 100644
index 000000000..b88dfae91
--- /dev/null
+++ b/packages/react/src/alert/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@opentiny/react-alert",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@opentiny/vue-renderless": "workspace:~",
+ "@opentiny/react-common": "workspace:~",
+ "@opentiny/react-icon": "workspace:~",
+ "@opentiny/vue-theme": "workspace:~"
+ }
+}
\ No newline at end of file
diff --git a/packages/react/src/alert/src/index.ts b/packages/react/src/alert/src/index.ts
new file mode 100644
index 000000000..4023bcdd1
--- /dev/null
+++ b/packages/react/src/alert/src/index.ts
@@ -0,0 +1,15 @@
+import pc from './pc.jsx'
+import mobileFirst from './mobile-first.jsx'
+
+export default function (props) {
+ const {
+ tiny_mode = 'pc'
+ } = props
+
+ const S = ({
+ pc,
+ 'mobile-first': mobileFirst
+ })[tiny_mode]
+
+ return (S(props))
+}
diff --git a/packages/react/src/alert/src/mobile-first.jsx b/packages/react/src/alert/src/mobile-first.jsx
new file mode 100644
index 000000000..15af50778
--- /dev/null
+++ b/packages/react/src/alert/src/mobile-first.jsx
@@ -0,0 +1,192 @@
+import { renderless, api } from '@opentiny/vue-renderless/alert/vue'
+import { IconClose, IconSuccess, IconError, IconHelp, IconWarning, IconChevronDown } from '@opentiny/react-icon'
+import {
+ vc,
+ If,
+ Component,
+ Slot,
+ useSetup,
+ useVm,
+} from '@opentiny/react-common'
+
+const $constants = {
+ ICON_MAP: {
+ success: IconSuccess,
+ error: IconError,
+ info: IconHelp,
+ warning: IconWarning
+ },
+ TITLE_MAP: {
+ success: 'ui.alert.success',
+ error: 'ui.alert.error',
+ info: 'ui.alert.info',
+ warning: 'ui.alert.warning'
+ },
+ CONTENT_MAXHEUGHT: 252
+}
+
+export default function Alert(props) {
+ const {
+ type = 'success',
+ size = 'normal',
+ center = false,
+ showIcon = true,
+ description = '',
+ slots = {},
+ _constants = $constants,
+ closable = true,
+ closeText,
+ title,
+ showFoldable = true,
+ singleLine,
+ scrolling
+ } = props
+
+ const defaultProps = {
+ type,
+ size,
+ center,
+ showIcon,
+ description,
+ slots,
+ _constants,
+ closable,
+ closeText,
+ title,
+ showFoldable
+ }
+
+ const { ref, current: vm, parent } = useVm()
+
+ const {
+ state,
+ handleHeaderClick
+ } = useSetup({
+ props: defaultProps,
+ renderless,
+ api,
+ constants: _constants,
+ vm,
+ parent
+ })
+
+ return (
+
+
+
+
+
+
+
+
+ {state.getTitle}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {state.getTitle}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {closeText}
+
+
+
+
)
+}
diff --git a/packages/react/src/alert/src/pc.jsx b/packages/react/src/alert/src/pc.jsx
new file mode 100644
index 000000000..b92a9d952
--- /dev/null
+++ b/packages/react/src/alert/src/pc.jsx
@@ -0,0 +1,128 @@
+import { renderless, api } from '@opentiny/vue-renderless/alert/vue'
+import { IconClose, IconSuccess, IconError, IconHelp, IconWarning } from '@opentiny/react-icon'
+import '@opentiny/vue-theme/alert/index.less'
+import {
+ vc,
+ If,
+ Component,
+ Slot,
+ useSetup,
+ useVm
+} from '@opentiny/react-common'
+
+const $constants = {
+ ICON_MAP: {
+ success: IconSuccess,
+ error: IconError,
+ info: IconHelp,
+ warning: IconWarning
+ },
+ TITLE_MAP: {
+ success: 'ui.alert.success',
+ error: 'ui.alert.error',
+ info: 'ui.alert.info',
+ warning: 'ui.alert.warning'
+ },
+ CONTENT_MAXHEUGHT: 252
+}
+
+export default function Alert(props) {
+ const {
+ type = 'success',
+ size = 'normal',
+ center = false,
+ showIcon = true,
+ description = '',
+ slots = {},
+ _constants = $constants,
+ closable = true,
+ closeText,
+ title
+ } = props
+
+ const defaultProps = {
+ type,
+ size,
+ center,
+ showIcon,
+ description,
+ slots,
+ _constants,
+ closable,
+ closeText,
+ title
+ }
+
+ const { ref, current: vm, parent } = useVm()
+
+ const {
+ state,
+ handleClose
+ } = useSetup({
+ props: defaultProps,
+ renderless,
+ api,
+ constants: _constants,
+ vm,
+ parent
+ })
+
+ return (
+
+
+
+
+
+
+
+
+ {state.getTitle}
+
+
+
+
+
+ {description}
+
+
+
+
+
+
+
+
+
+
+
+
+ {closeText}
+
+
+
+
)
+}
diff --git a/packages/react/src/button/index.ts b/packages/react/src/button/index.ts
new file mode 100644
index 000000000..6dada6f44
--- /dev/null
+++ b/packages/react/src/button/index.ts
@@ -0,0 +1,3 @@
+import Button from './src'
+
+export default Button
diff --git a/packages/react/src/button/package.json b/packages/react/src/button/package.json
new file mode 100644
index 000000000..6215539ca
--- /dev/null
+++ b/packages/react/src/button/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@opentiny/react-button",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@opentiny/vue-renderless": "workspace:~",
+ "@opentiny/vue-theme": "workspace:~",
+ "@opentiny/react-common": "workspace:~",
+ "@opentiny/react-icon": "workspace:~",
+ "@opentiny/vue-theme-mobile": "workspace:~"
+ }
+}
diff --git a/packages/react/src/button/src/index.ts b/packages/react/src/button/src/index.ts
new file mode 100644
index 000000000..623b65a02
--- /dev/null
+++ b/packages/react/src/button/src/index.ts
@@ -0,0 +1,17 @@
+import pc from './pc.jsx'
+import mobile from './mobile.jsx'
+import mobileFirst from './mobile-first.jsx'
+
+export default function (props) {
+ const {
+ tiny_mode = 'pc'
+ } = props
+
+ const S = ({
+ pc,
+ mobile,
+ 'mobile-first': mobileFirst
+ })[tiny_mode]
+
+ return (S(props))
+}
\ No newline at end of file
diff --git a/packages/react/src/button/src/mobile-first.jsx b/packages/react/src/button/src/mobile-first.jsx
new file mode 100644
index 000000000..1218fb0e0
--- /dev/null
+++ b/packages/react/src/button/src/mobile-first.jsx
@@ -0,0 +1,107 @@
+import { renderless, api } from '@opentiny/vue-renderless/button/vue'
+import { useSetup, vc, If, Component, Slot, useVm } from '@opentiny/react-common'
+import { IconLoading } from '@opentiny/react-icon'
+import { classes } from './token'
+
+const define_props = [
+ 'children',
+ 'text',
+ 'loading',
+ 'autofocus',
+ 'plain',
+ 'round',
+ 'circle',
+ 'icon',
+ 'size',
+ 'type',
+ 'nativeType',
+ 'resetTime',
+ /^on/
+]
+
+export default function Button(props) {
+ const {
+ loading,
+ autofocus,
+ size,
+ icon,
+ round,
+ circle,
+ href,
+ buttonClass,
+ tabindex,
+ text,
+ type,
+ nativeType = 'button',
+ resetTime = 1000
+ } = props
+
+ const defaultProps = Object.assign({
+ nativeType,
+ resetTime,
+ }, props)
+
+ const {
+ ref,
+ parent,
+ current: vm
+ } = useVm()
+
+ const {
+ handleClick,
+ state,
+ a,
+ m,
+ gcls,
+ } = useSetup({
+ api,
+ renderless,
+ props: defaultProps,
+ classes,
+ ref,
+ parent,
+ vm
+ })
+
+ const $attrs = a(props, define_props, false)
+
+ return (
+
+
+
+
+
+
+ {text}
+
+
+ )
+}
diff --git a/packages/react/src/button/src/mobile.jsx b/packages/react/src/button/src/mobile.jsx
new file mode 100644
index 000000000..e464bbb1f
--- /dev/null
+++ b/packages/react/src/button/src/mobile.jsx
@@ -0,0 +1,88 @@
+import { renderless, api } from '@opentiny/vue-renderless/button/vue'
+import { useSetup, vc, If, Component, Slot, useVm } from '@opentiny/react-common'
+import { IconLoading } from '@opentiny/react-icon'
+import '@opentiny/vue-theme-mobile/button/index.less'
+
+const define_props = [
+ 'children',
+ 'text',
+ 'loading',
+ 'autofocus',
+ 'plain',
+ 'round',
+ 'circle',
+ 'icon',
+ 'size',
+ 'type',
+ 'nativeType',
+ 'resetTime',
+ /^on/
+]
+
+export default function Button(props) {
+ const {
+ text,
+ loading,
+ round,
+ icon,
+ size,
+ type = 'default',
+ nativeType = 'button',
+ resetTime = 1000
+ } = props
+
+ const defaultProps = Object.assign({
+ type,
+ nativeType,
+ resetTime
+ }, props)
+
+ const {
+ ref,
+ parent,
+ current: vm
+ } = useVm()
+
+ const {
+ handleClick,
+ state,
+ a
+ } = useSetup({
+ props: defaultProps,
+ renderless,
+ api,
+ parent,
+ vm
+ })
+
+ const $attrs = a(props, define_props, false)
+
+ return (
+
+
+
+
+
+
+ { text }
+
+
+ )
+}
diff --git a/packages/react/src/button/src/pc.jsx b/packages/react/src/button/src/pc.jsx
new file mode 100644
index 000000000..f3c322f04
--- /dev/null
+++ b/packages/react/src/button/src/pc.jsx
@@ -0,0 +1,99 @@
+import { renderless, api } from '@opentiny/vue-renderless/button/vue'
+import { useSetup, If, Component, vc, useVm } from '@opentiny/react-common'
+import { IconLoading } from '@opentiny/react-icon'
+import '@opentiny/vue-theme/button/index.less'
+
+const define_props = [
+ 'children',
+ 'text',
+ 'loading',
+ 'autofocus',
+ 'plain',
+ 'round',
+ 'circle',
+ 'icon',
+ 'size',
+ 'type',
+ 'nativeType',
+ 'resetTime',
+ /^on/
+]
+
+export default function Button(props) {
+ const {
+ children,
+ text,
+ loading,
+ autofocus,
+ round,
+ circle,
+ icon,
+ size,
+ tabindex,
+ type = 'default',
+ nativeType = 'button',
+ resetTime = 1000
+ } = props
+
+ const defaultProps = Object.assign({
+ type,
+ nativeType,
+ resetTime
+ }, props)
+
+ const {
+ ref,
+ parent,
+ current: vm
+ } = useVm()
+
+ const {
+ handleClick,
+ state,
+ a
+ } = useSetup({
+ props: defaultProps,
+ renderless,
+ api,
+ vm,
+ parent
+ })
+
+ const $attrs = a(props, define_props, false)
+
+ return (
+
+
+
+
+
+ {children || text}
+
+ )
+}
diff --git a/packages/react/src/button/src/token.ts b/packages/react/src/button/src/token.ts
new file mode 100644
index 000000000..610775dd0
--- /dev/null
+++ b/packages/react/src/button/src/token.ts
@@ -0,0 +1,66 @@
+export const classes = {
+ 'button':
+ 'inline-block sm:max-w-[9rem] text-center overflow-hidden overflow-ellipsis whitespace-nowrap transition-button duration-300 delay-[0ms]',
+ 'size-default': 'h-10 text-sm sm:h-7 sm:text-xs',
+ 'size-medium': 'h-10 text-sm sm:h-8 sm:text-xs',
+ 'size-small': 'h-8 text-sm sm:h-7 sm:text-xs',
+ 'size-mini': 'h-7 sm:h-6 sm:text-xs',
+ 'type-default':
+ 'text-black border-color-border hover:border-color-border-hover active:border-color-border-active sm:cursor-pointer',
+ 'type-primary':
+ 'text-white border-color-brand bg-color-brand hover:border-color-brand-hover hover:bg-color-brand-hover active:border-color-brand-active active:bg-color-brand-active sm:cursor-pointer',
+ 'type-success':
+ 'text-white border-color-success bg-color-success hover:border-color-success-hover hover:bg-color-success-hover active:border-color-success-active active:bg-color-success-active sm:cursor-pointer',
+ 'type-info':
+ 'text-white border-color-info-secondary bg-color-info-secondary hover:border-color-info-secondary-hover hover:bg-color-info-secondary-hover active:border-color-info-secondary-active active:bg-color-info-secondary-active sm:cursor-pointer',
+ 'type-warning':
+ 'text-white border-color-warning bg-color-warning hover:border-color-warning-hover hover:bg-color-warning-hover active:border-color-warning-active active:bg-color-warning-active sm:cursor-pointer',
+ 'type-danger':
+ 'text-white border-color-error bg-color-error hover:border-color-error-hover hover:bg-color-error-hover active:border-color-error-active active:bg-color-error-active sm:cursor-pointer',
+ 'type-text':
+ 'border-none bg-transparent cursor-pointer text-color-text-placeholder active:text-color-text-primary sm:hover:text-color-text-primary sm:active:!text-color-brand-active',
+ 'type-default-disabled': 'text-color-text-disabled bg-color-bg-3 border-transparent hover:cursor-not-allowed',
+ 'type-primary-disabled': 'text-white bg-color-brand-disabled border-transparent hover:cursor-not-allowed',
+ 'type-success-disabled': 'text-white bg-color-success-disabled border-transparent hover:cursor-not-allowed',
+ 'type-info-disabled': 'text-white bg-color-info-secondary-disabled border-transparent hover:cursor-not-allowed',
+ 'type-warning-disabled': 'text-white bg-color-alert-disabled border-transparent hover:cursor-not-allowed',
+ 'type-danger-disabled': 'text-white bg-color-error-disabled border-transparent hover:cursor-not-allowed',
+ 'type-text-disabled': 'text-color-text-disabled hover:cursor-not-allowed',
+ 'type-default-plain':
+ 'text-black border-color-border hover:border-color-border-hover active:border-color-border-active sm:cursor-pointer',
+ 'type-primary-plain':
+ 'text-color-brand border-color-brand hover:text-color-brand-hover hover:border-color-brand-hover active:text-color-brand-active active:border-color-brand-active bg-white sm:cursor-pointer',
+ 'type-success-plain':
+ 'text-color-success border-color-success hover:text-color-success-hover hover:border-color-success-hover active:text-color-success-active active:border-color-success-active bg-white sm:cursor-pointer',
+ 'type-info-plain':
+ 'text-color-info-secondary border-color-info-secondary hover:text-color-info-secondary-hover hover:border-color-info-secondary-hover active:text-color-info-secondary-active active:border-color-info-secondary-active bg-white sm:cursor-pointer',
+ 'type-warning-plain':
+ 'text-color-warning border-color-warning hover:text-color-warning-hover hover:border-color-warning-hover active:text-color-warning-active active:border-color-warning-active bg-white sm:cursor-pointer',
+ 'type-danger-plain':
+ 'text-color-error border-color-error hover:text-color-error-hover hover:border-color-error-hover active:text-color-error-active active:border-color-error-active bg-white sm:cursor-pointer',
+ 'type-text-plain': 'text-color-brand hover:text-color-brand-hover active:text-color-brand-active',
+ 'type-default-plain-disabled':
+ 'text-color-text-disabled bg-white border-color-text-disabled hover:cursor-not-allowed',
+ 'type-primary-plain-disabled':
+ 'text-color-brand-disabled bg-white border-color-brand-disabled hover:cursor-not-allowed',
+ 'type-success-plain-disabled':
+ 'text-color-success-disabled bg-white border-color-success-disabled hover:cursor-not-allowed',
+ 'type-info-plain-disabled':
+ 'text-color-info-secondary-disabled bg-white border-color-info-secondary-disabled hover:cursor-not-allowed',
+ 'type-warning-plain-disabled':
+ 'text-color-alert-disabled bg-white border-color-alert-disabled hover:cursor-not-allowed',
+ 'type-danger-plain-disabled':
+ 'text-color-error-disabled bg-white border-color-error-disabled hover:cursor-not-allowed',
+ 'type-text-plain-disabled': 'text-color-text-disabled hover:cursor-not-allowed',
+ 'no-round': 'rounded-sm',
+ 'is-round': 'rounded-full',
+ 'is-border': 'border-0.5 sm:border',
+ 'no-circle': 'sm:min-w-[4.5rem] pl-3 pr-3',
+ 'is-circle': 'sm:min-w-[0.4375rem] sm:rounded-full sm:pl-2 sm:pr-2',
+ 'button-icon': '-mt-0.5 sm:text-base fill-current',
+ 'button-icon-default': 'text-color-icon-primary hover:text-color-icon-hover active:text-color-icon-active',
+ 'button-icon-disabled': 'text-color-icon-disabled hover:cursor-not-allowed',
+ 'loading-svg': 'animate-spin-2 mr-1 fill-current -inset-0.5',
+ 'button-link':
+ 'text-color-link hover:text-color-link-hover active:color-link-hover active:hover:text-color-link-hover sm:hover:text-color-link-hover'
+}
diff --git a/packages/renderless/src/checkbox/react.ts b/packages/renderless/src/checkbox/react.ts
new file mode 100644
index 000000000..35b03dc10
--- /dev/null
+++ b/packages/renderless/src/checkbox/react.ts
@@ -0,0 +1,22 @@
+// import {
+// addToStore,
+// removeFromStore,
+// handleChange,
+// computedGetModelGet,
+// computedGetModelSet,
+// computedIsChecked,
+// computedIsGroup,
+// computedStore,
+// computedIsLimitDisabled,
+// computedIsDisabled,
+// computedIsDisplayOnly,
+// computedIsGroupDisplayOnly,
+// computedFormItemSize,
+// computedCheckboxSize,
+// mounted,
+// toggleEvent,
+// dispatchDisplayedValue,
+// getDisplayedValue,
+// computedIsShowText,
+// computedShowText
+// } from './index'
diff --git a/packages/renderless/tsconfig.json b/packages/renderless/tsconfig.json
index 58a950440..6e8d56081 100644
--- a/packages/renderless/tsconfig.json
+++ b/packages/renderless/tsconfig.json
@@ -25,5 +25,5 @@
},
"include": [
"src/**/*.ts"
- ]
+, "src/button/react.js" ]
}
diff --git a/packages/theme/scripts/build-component-cssvar.js b/packages/theme/scripts/build-component-cssvar.js
index ddf50c7d3..de723fa30 100644
--- a/packages/theme/scripts/build-component-cssvar.js
+++ b/packages/theme/scripts/build-component-cssvar.js
@@ -3,6 +3,20 @@ const fg = require('fast-glob')
const ignoreNames = ['base', 'theme']
+function groupBy(array, fn) {
+ const map = {}
+ array.forEach((item) => {
+ const key = fn(item)
+ const collection = map[key]
+ if (!collection) {
+ map[key] = [item]
+ } else {
+ collection.push(item)
+ }
+ })
+ return map
+}
+
fg(['**/*-theme.js']).then((files) => {
const components = files
.map((file) => {
@@ -40,17 +54,3 @@ fg(['**/*-theme.js']).then((files) => {
fs.writeFileSync(`src/theme/${themeKey}/component.js`, contents)
})
})
-
-function groupBy(array, fn) {
- const map = {}
- array.forEach((item) => {
- const key = fn(item)
- const collection = map[key]
- if (!collection) {
- map[key] = [item]
- } else {
- collection.push(item)
- }
- })
- return map
-}
diff --git a/packages/vue-common/src/adapter/vue2/index.ts b/packages/vue-common/src/adapter/vue2/index.ts
index e26bccf07..2d9eb47cd 100644
--- a/packages/vue-common/src/adapter/vue2/index.ts
+++ b/packages/vue-common/src/adapter/vue2/index.ts
@@ -216,6 +216,7 @@ const createVm = (vm, instance, context = undefined) => {
export const tools = (context, mode) => {
const instance = hooks.getCurrentInstance()?.proxy as any
+ console.log(instance)
const root = instance?.$root
const { route, router } = useRouter(instance)
const i18n = root?.$i18n