diff --git a/examples/solid-docs/index.html b/examples/solid-docs/index.html
new file mode 100644
index 000000000..b9a46f405
--- /dev/null
+++ b/examples/solid-docs/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Opentiny Solid 组件调试
+
+
+
+
+
+
diff --git a/examples/solid-docs/package.json b/examples/solid-docs/package.json
new file mode 100644
index 000000000..a55ab94ec
--- /dev/null
+++ b/examples/solid-docs/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "@opentiny/docs-solid",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "solid-js": "^1.7.8",
+ "@opentiny/solid": "workspace:~"
+ },
+ "devDependencies": {
+ "vite": "^4.4.5",
+ "vite-plugin-solid": "^2.7.0",
+ "vite-plugin-svgr": "^3.2.0"
+ }
+}
diff --git a/examples/solid-docs/public/vite.svg b/examples/solid-docs/public/vite.svg
new file mode 100644
index 000000000..e7b8dfb1b
--- /dev/null
+++ b/examples/solid-docs/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/solid-docs/src/App.tsx b/examples/solid-docs/src/App.tsx
new file mode 100644
index 000000000..967bda74b
--- /dev/null
+++ b/examples/solid-docs/src/App.tsx
@@ -0,0 +1,12 @@
+import { Button } from '@opentiny/solid'
+
+// 在这里导入组件,进行 api 调试
+function App() {
+ return (
+
+
+
+ )
+}
+
+export default App
diff --git a/examples/solid-docs/src/main.css b/examples/solid-docs/src/main.css
new file mode 100644
index 000000000..50154555f
--- /dev/null
+++ b/examples/solid-docs/src/main.css
@@ -0,0 +1,4 @@
+.app {
+ margin: 10px;
+ width: 500px;
+}
diff --git a/examples/solid-docs/src/main.tsx b/examples/solid-docs/src/main.tsx
new file mode 100644
index 000000000..2cda43153
--- /dev/null
+++ b/examples/solid-docs/src/main.tsx
@@ -0,0 +1,7 @@
+import { render } from 'solid-js/web'
+import App from './App'
+import './main.css'
+
+const root = document.getElementById('root')
+
+render(() => , root)
diff --git a/examples/solid-docs/tsconfig.json b/examples/solid-docs/tsconfig.json
new file mode 100644
index 000000000..a7fc6fbf2
--- /dev/null
+++ b/examples/solid-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/solid-docs/tsconfig.node.json b/examples/solid-docs/tsconfig.node.json
new file mode 100644
index 000000000..42872c59f
--- /dev/null
+++ b/examples/solid-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/solid-docs/vite.config.ts b/examples/solid-docs/vite.config.ts
new file mode 100644
index 000000000..163b240eb
--- /dev/null
+++ b/examples/solid-docs/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import solid from 'vite-plugin-solid'
+import svgr from 'vite-plugin-svgr'
+
+export default defineConfig({
+ plugins: [solid(), svgr()]
+})
diff --git a/package.json b/package.json
index 5f16d7c0f..0f8922b7a 100644
--- a/package.json
+++ b/package.json
@@ -124,7 +124,9 @@
"build:react-site": "pnpm --filter @opentiny/react-site build",
"prettier": "prettier --config .prettierrc --write .",
"// ---------- openinula 相关脚本命令 ----------": "",
- "dev:openinula": "pnpm -C examples/openinula-docs run dev"
+ "dev:openinula": "pnpm -C examples/openinula-docs run dev",
+ "// ---------- solid 相关脚本命令 ----------": "",
+ "dev:solid": "pnpm -C examples/solid-docs run dev"
},
"dependencies": {
"@vue/composition-api": "1.2.2",
diff --git a/packages/solid/.depcheckrc.yaml b/packages/solid/.depcheckrc.yaml
new file mode 100644
index 000000000..93b829947
--- /dev/null
+++ b/packages/solid/.depcheckrc.yaml
@@ -0,0 +1,2 @@
+ignores:
+ - '@opentiny/solid*'
diff --git a/packages/solid/index.ts b/packages/solid/index.ts
new file mode 100644
index 000000000..91703514d
--- /dev/null
+++ b/packages/solid/index.ts
@@ -0,0 +1,9 @@
+import Button from '@opentiny/solid-button'
+
+export const version = '1.0.0'
+
+export { Button }
+
+export default {
+ Button
+} as any
diff --git a/packages/solid/package.json b/packages/solid/package.json
new file mode 100644
index 000000000..8e4e72fd4
--- /dev/null
+++ b/packages/solid/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "@opentiny/solid",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.ts",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@opentiny/solid-common": "workspace:~",
+ "@opentiny/solid-button": "workspace:~"
+ }
+}
diff --git a/packages/solid/src/button/index.ts b/packages/solid/src/button/index.ts
new file mode 100644
index 000000000..9bc345130
--- /dev/null
+++ b/packages/solid/src/button/index.ts
@@ -0,0 +1,3 @@
+import Alert from './src'
+
+export default Alert
diff --git a/packages/solid/src/button/package.json b/packages/solid/src/button/package.json
new file mode 100644
index 000000000..5eab01aa5
--- /dev/null
+++ b/packages/solid/src/button/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@opentiny/solid-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/solid-common": "workspace:~",
+ "@opentiny/vue-theme": "workspace:~",
+ "@opentiny/vue-theme-mobile": "workspace:~"
+ }
+}
diff --git a/packages/solid/src/button/src/index.ts b/packages/solid/src/button/src/index.ts
new file mode 100644
index 000000000..764fd362c
--- /dev/null
+++ b/packages/solid/src/button/src/index.ts
@@ -0,0 +1,11 @@
+import pc from './pc'
+
+export default function (props) {
+ const { tiny_mode = 'pc' } = props
+
+ const S = {
+ pc
+ }[tiny_mode]
+
+ return S(props)
+}
diff --git a/packages/solid/src/button/src/pc.jsx b/packages/solid/src/button/src/pc.jsx
new file mode 100644
index 000000000..c198e619b
--- /dev/null
+++ b/packages/solid/src/button/src/pc.jsx
@@ -0,0 +1,35 @@
+import { renderless } from '@opentiny/vue-renderless/button/vue'
+import { useSetup } from '@opentiny/solid-common'
+import '@opentiny/vue-theme/button/index.less'
+
+export default function Button(props) {
+ const { children, text, autofocus, round, circle, icon: Icon, size, nativeType = 'button' } = props
+ const { handleClick, state, tabindex, type, $attrs } = useSetup({
+ props: { nativeType: 'button', resetTime: 1000, ...props },
+ renderless
+ })
+
+ return (
+
+ )
+}
diff --git a/packages/solid/src/common/package.json b/packages/solid/src/common/package.json
new file mode 100644
index 000000000..0e36a8e6a
--- /dev/null
+++ b/packages/solid/src/common/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@opentiny/solid-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",
+ "solid-js": "^1.7.8"
+ }
+}
diff --git a/packages/solid/src/common/src/index.ts b/packages/solid/src/common/src/index.ts
new file mode 100644
index 000000000..83156f642
--- /dev/null
+++ b/packages/solid/src/common/src/index.ts
@@ -0,0 +1,91 @@
+import * as hooks from 'solid-js'
+import { createSignal, onCleanup, createMemo } from 'solid-js'
+import '@opentiny/vue-theme/base/index.less'
+
+const EVENTS_PREFIX = 'on'
+
+// 处理solid事件触发机制
+export const emit =
+ (props) =>
+ (evName, ...args) => {
+ const eventsName = `${EVENTS_PREFIX}${evName[0].toLocaleUpperCase()}${evName.slice(1)}`
+ if (props[eventsName] && typeof props[eventsName] === 'function') {
+ props[eventsName](...args)
+ }
+ }
+
+export const useSetState = (initialState) => {
+ const [state, setState] = createSignal(initialState, { equals: false })
+
+ return [state, setState]
+}
+
+// props 应该不用做处理, props 都是 . 访问。
+export const reactive = (staticObject) => {
+ const [state, setState] = useSetState(staticObject)
+
+ return new Proxy(state(), {
+ get(target, property) {
+ if (property === 'solidState') {
+ return state
+ }
+ if (typeof target[property] === 'function') {
+ return target[property](target)
+ } else {
+ return target[property]
+ }
+ },
+ set(target, property, value) {
+ Reflect.set(target, property, value)
+ setState((val) => val)
+ return true
+ }
+ })
+}
+
+// nextTick, 等待 dom 更新后触发回调
+export const useNextTick = (callback) => {
+ queueMicrotask(callback)
+}
+
+// emitEvent, dispath, broadcast
+export const emitEvent = () => {
+ const broadcast = () => {
+ return ''
+ }
+
+ return {
+ dispatch: () => {
+ return ''
+ },
+ broadcast
+ }
+}
+
+const computed = (callback) => {
+ try {
+ return createMemo(callback)
+ } catch (error) {
+ return []
+ }
+}
+
+export const useSetup = ({ props, renderless, extendOptions = { framework: 'Solid' } }) => {
+ const render = typeof props.tiny_renderless === 'function' ? props.tiny_renderless : renderless
+ const utils = {
+ parent: {},
+ emit: emit(props)
+ }
+ const sdk = render(
+ props,
+ { ...hooks, reactive, computed, useNextTick, inject: () => {}, watch: () => {}, onBeforeUnmount: onCleanup },
+ utils,
+ extendOptions
+ )
+
+ return {
+ ...sdk,
+ state: sdk.state.solidState,
+ type: props.type ?? 'default'
+ }
+}