diff --git a/docs/areact/getting-started.mdx b/docs/areact/getting-started.mdx new file mode 100644 index 0000000..68573ad --- /dev/null +++ b/docs/areact/getting-started.mdx @@ -0,0 +1,219 @@ +--- +title: 快速上手 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# 快速上手 +:::warning +本文默认你已经掌握 **ArenaPro(或者ArenaLess)** 插件的基本用法,如未了解,请先阅读[ArenaPro](https://www.yuque.com/box3lab/arenapro)插件文档。 +::: +本篇文章将让您快速入门Areact,从项目创建、配置到开发、部署。 + +## 环境要求 +1. VSCode(版本尽可能在1.93左右) + :::tip + 如果无法使用桌面端VSCode配合AP/AL进行开发,那么可以试一试[vscode.dev](https://vscode.dev)+[ArenaLess](/docs/arenaless/)的组合 + ::: +2. VSCode扩展 [ArenaPro(AP)](https://www.yuque.com/box3lab/arenapro)或者[ArenaLess(AL)](/docs/arenaless/) + +## 创建项目并完成JSX配置 +### 1. 创建项目 +在VSCode中按下Ctrl+Shift+P,输入命令并找到创建项目的命令,运行它。 +:::note +- 在 ArenaPro 中,这个命令叫`ArenaPro: 新建Arena-Ts项目` +- 在 ArenaLess 中,这个命令叫`ArenaLess: 创建ArenaLess项目` + +这两个扩展创建的项目结构几乎是相同的 +::: +### 2. 修改文件名和入口点 +- 把 `client/src/clientApp.ts` 重命名为-> `client/src/clientApp.tsx` +- 将 `dao3.config.json` 中的 `client->entry` 改为 `src/clientApp.tsx`(类似下面这样) + ```js title="dao3.config.json" + { + "client": { + "base": "./client", + // 修改这里 + "entry": "src/clientApp.tsx", + //...省略... + } + } + ``` +### 3. 修改client/tsconfig.json +需要在`compilerOptions`中加入一些`jsx/tsx+areact`相关的配置 +```js title="client/tsconfig.json" + { + "compilerOptions": { + // ...省略... + // 这四行是要添加的内容 + "noImplicitAny": false, + "jsx": "react", + "jsxFactory": "Areact.h", + "jsxFragmentFactory": "Areact.frag" + // ...省略... + } + } +``` +:::info +请记住,如果你下次还要创建一个这样的项目的话依旧要完成以上步骤 +::: +## 安装 Areact +对于ArenaPro和ArenaLess有不同的安装步骤。 + + +```sh title="在项目根目录打开VSCode的终端,输入以下命令:" +npm install --save dao3-areact +``` + + +```js title='在importMap.arenaless.jsonc的"imports"中添加:' +{ + "imports":{ + // 在这里加入一条导入 + "dao3-areact":"npm:dao3-areact" + } +} +``` + + + +## 你的第一段代码 +好了,所有的配置工作完工了,现在开始写你的第一段代码吧! +
+ 点击查看完整代码 +```tsx title="client/src/clientApp.tsx" +import { Areact, hooks } from "dao3-areact"; +import { Text } from "dao3-areact/components"; +let app = new Areact(); +function App() { + const [count,setCount]=hooks.useState(0); + return (<> + {setCount(count+1)}} + > + ); +} +app.mount(, ui); +``` +
+ +> 接下来是分解教程 +### 1. 导入 +```tsx +import { Areact, hooks } from "dao3-areact"; +import { Text } from "dao3-areact/components"; +``` +- `Areact`是框架 +- `hooks`是`react(preact)`的`hooks`,详见[Built-in React Hooks(React文档)](https://react.dev/reference/react/hooks) +- `Text`是一个文本组件,就类似原岛三ui中的UiText,以下是所有的组件: + - `Text` 显示文本的组件 + - `Image` 显示图片的组件 + - `Input` 显示输入框的组件 + - `Box` 一个框架组件,没有什么特征 +### 2. 创建 入口点组件 +```tsx +let app = new Areact(); +function App() { + // 待会添加的内容 + return (<> + {/* ... 这里是待会要填充的组件 ...*/} + ); +} +// 将入口点组件挂载到Areact上运行渲染 +app.mount(,ui) +``` +最基础的部分已经ok了,但是你现在运行见不到任何效果,那是因为你还没有添加其他组件。 +### 3. 文本组件 +接下来,我们修改`function App(){}`的内容,添加一个文本组件: +```tsx +function App() { + // 待会添加的内容 + return (<> + + ); +} +``` +
+ 讲解一下以上的各个属性(属性值大多用字符串) +:::info +1. `x`,`y`,`width`,`height`这些属性的格式是这样的:`<偏移数值>px+<百分比比例>%`,其中可以把`+`换成`-`,甚至多加几个`+和-`都是允许的。 + + 最后会被转换为`offset`和`scale`值。例子`10px`,`10px+20%`,`10px+20%-20px+10%` +2. `text` 这是适用于`Text`和`Input`的组件的属性,代表文本内容。相当于岛三ui的`textContent`。 + + 因为react占用了`textContent`这个属性,所以我们用了`text`代替。 +3. `backgroundColor`,`textColor` 前者是背景颜色,后者是文本颜色。接收css的颜色值,会自动转换为rgb的vec3。 +4. `backgroundOpacity` 背景不透明度,接受百分数 +::: +
+好,现在左上角出现了一个黑色底,白色字的文本框。 +### 4. 添加事件和state +一个文本框太过单调了……如果我们想点击它改变显示的文字呢?例如点击显示的数字+1。 +- 使用`hooks.useState()`来创建一个状态变量,当这个变量使用`setXXX`更新时,会重新渲染使用了该变量的组件, + 这样我们在点击的时候把`count`变量+1,文本框的文本会随之变化。 +- 使用`onClick`属性来注册点击事件,并且更新状态变量`count` + +这是`useState`的一般用法: +```tsx +let [name,setName]=hooks.useState("tobylai");// tobylai就是你指定的默认值 +// 前者是用于读取的变量,后者是更新变量时所用的函数 +// 这个命名取决于你,规范的写法是[aaa,setAaa] +console.log(name); // "tobylai" +setName("Areact"); // 这里更新了状态变量 +console.log(name); // "Areact" +``` +让我们修改先前的代码,得到最终成品: +```tsx +import { Areact, hooks } from "dao3-areact"; +import { Text } from "dao3-areact/components"; +let app = new Areact(); +function App() { + // 这个状态变量count初始值为0,调用setCount可以改变它 + const [count,setCount]=hooks.useState(0); + return (<> + {setCount(count+1)}}// onClick事件,把count设置为count+1 + > + ); +} +app.mount(, ui); +``` +现在你就得到了一个点击就能+1的文本框了,像不像个电子木鱼? + +## 更远……文本框+输入框 +以下代码可以实现在输入框输入文字的过程中更新文本框的内容 +```tsx +import { Areact, hooks } from "dao3-areact"; +import { Text,Input,Box,Image } from "dao3-areact/components"; +let app = new Areact(); + +function App() { + const [name,setName]=hooks.useState(""); + return (<> + + setName(e.target.getAttribute("text"))} // 使用onInput事件监听输入 + text={name} // 绑定是双向的~ + > + ); +} +app.mount(, ui); +``` \ No newline at end of file diff --git a/docs/areact/index.md b/docs/areact/index.md new file mode 100644 index 0000000..0ca39fc --- /dev/null +++ b/docs/areact/index.md @@ -0,0 +1,56 @@ +--- +title: Areact +--- + +# Areact +:::warning 使用前三思 +目前神岛client端编辑器的优化很烂,过大的体积会导致地图无法打开。而目前此库体积`256kb`左右,有几率导致崩溃…… +- 呼叫大板砖支援 +- 呼叫大板砖支援 +- 呼叫大板砖支援 +::: +Areact是一个基于Preact的UI框架,能够在[神奇代码岛](https://dao3.fun)上利用类似React的方式创建UI界面。 + +## 快速开始 +:::info[快速上手] +需要新手入门教程?请点击[这里](./getting-started) +::: + + +## 速览(简单示例) +![1726978076891](index/1726978076891.png) +```tsx title="clientApp.tsx" +import { Areact, hooks } from "dao3-areact"; +import { Text } from "dao3-areact/components"; +let app = new Areact(); +// 这是UI的根组件 +function App() { + const [count,setCount]=hooks.useState(0); + return (<> + {/* 文字组件,点击+1 */} + {setCount(count+1)}} + > + ); +} +// 挂载根组件 开始渲染 +app.mount(, ui); +``` + +## 优势 +- 与[ArenaPro](https://www.yuque.com/box3lab/arenapro)或者[ArenaLess](/docs/arenaless/)结合使用,TypeScript助力开发 +- React 的写法,有React的优点,例如状态更新等 +- 支持`React`的`Hooks`,例如`useState`、`useEffect`等 +- 事件写法更为清晰,可以在节点上`onClick`,`onInput`等 +- 组件化的编程方式,十分简单地创建起属于你的组件库,避免了复制粘贴。 +- 近似于`html`的风格,有特色的组件参数绑定 + +## 缺点 +- 性能开销略高 +- 可能会出现bug +- 打包体积较大,输出`256kb`左右,可能会导致地图崩溃 +- 有一些神岛自带的组件无法使用,原因是搬砖没有写JavaScriptAPI,只做了编辑器…… \ No newline at end of file diff --git a/docs/areact/index/1726978076891.png b/docs/areact/index/1726978076891.png new file mode 100644 index 0000000..3b67e2b Binary files /dev/null and b/docs/areact/index/1726978076891.png differ diff --git a/docs/dao3-aui/getting-started.mdx b/docs/dao3-aui/getting-started.mdx index 0075153..be63549 100644 --- a/docs/dao3-aui/getting-started.mdx +++ b/docs/dao3-aui/getting-started.mdx @@ -6,6 +6,10 @@ sidebar_position: 2 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +:::danger 迁移! +dao3-aui不再维护,请迁移到Areact +::: + # 快速上手 :::warning 本文默认你已经掌握 **ArenaPro(或者ArenaLess)** 插件的基本用法,如未了解,请先阅读[ArenaPro](https://www.yuque.com/box3lab/arenapro)插件文档。 diff --git a/docs/dao3-aui/index.md b/docs/dao3-aui/index.md index bf34bda..3082d26 100644 --- a/docs/dao3-aui/index.md +++ b/docs/dao3-aui/index.md @@ -1,6 +1,11 @@ --- -title: Dao3-AUI (React on 神岛) +title: ⚠迁移 Dao3-AUI --- + +:::danger 迁移! +dao3-aui不再维护,请迁移到Areact +::: + # Dao3-AUI Dao3-AUI是**开源**的,如果你有需求或者bug汇报,请到[本项目repo](https://github.com/Box3TRC/dao3-aui)开issue。 ## 什么是Dao3-AUI ⚛️+📦 diff --git a/docusaurus.config.js b/docusaurus.config.js index 3746d35..14e17ac 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -5,6 +5,7 @@ // See: https://docusaurus.io/docs/api/docusaurus-config import { themes as prismThemes } from 'prism-react-renderer'; +import tabBlocks from "docusaurus-remark-plugin-tab-blocks"; /** @type {import('@docusaurus/types').Config} */ const config = { @@ -40,6 +41,7 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { + remarkPlugins: [[tabBlocks,{}]], sidebarPath: './sidebars.js', // Please change this to your repo. // Remove this to remove the "edit this page" links. @@ -47,6 +49,7 @@ const config = { 'https://github.com/Box3TRC/documentation/blob/main', }, blog: { + remarkPlugins: [tabBlocks], showReadingTime: true, // Please change this to your repo. // Remove this to remove the "edit this page" links. @@ -141,6 +144,38 @@ const config = { explicitSearchResultPath: true, }), ], + ],plugins:[ + // [ + // 'vercel-analytics', + // { + // debug: true, + // mode: 'auto', + // }, + // ], + // [ + // 'docusaurus-plugin-typedoc', + // { + // id: 'typedoc-areact', + // entryPoints: ['dts/areact/index.d.ts'], + // skipErrorChecking: true, + // name: "模块: areact", + // out: "/docs/areact/api/index", + // sidebar: { + // "autoConfiguration": false, + // "pretty": false + // } + // }, + // ], + // [ + // 'docusaurus-plugin-typedoc', + // { + // id: 'typedoc-areact-components', + // entryPoints: ['dts/areact/components.d.ts'], + // skipErrorChecking: true, + // name: "模块: areact/components", + // out: "/docs/areact/api/components" + // }, + // ], ] }; diff --git a/dts/areact/components.d.ts b/dts/areact/components.d.ts new file mode 100644 index 0000000..b761ae1 --- /dev/null +++ b/dts/areact/components.d.ts @@ -0,0 +1,57 @@ +import type { FunctionalComponent } from "preact"; +export interface AreactHTMLElement extends HTMLElement { + uiNode: UiRenderable; +} +export interface AreactEvent extends Event { + target: AreactHTMLElement; +} +interface UiRenderableOpt { + id?: string; + x?: string; + y?: string; + width?: string; + height?: string; + anchor?: string; + backgroundColor?: string; + backgroundOpacity?: number | string; + zIndex?: number | string; + autoResize?: 'NONE' | 'X' | 'Y' | 'XY'; + visible?: boolean | string; + pointerEventBehavior?: string; + onClick?: (e: AreactEvent) => void; +} +interface UiTextOpt extends UiRenderableOpt { + text?: string; + textFontSize?: number | string; + textColor?: string; + textXAlignment?: 'Center' | 'Left' | 'Right'; + textYAlignment?: 'Center' | 'Top' | 'Bottom'; + autoWordWrap?: boolean | string; + textLineHeight?: number | string; +} +export declare function Text(opt: UiTextOpt & { + children?: any; +}): FunctionalComponent; +interface UiBoxOpt extends UiRenderableOpt { +} +export declare function Box(opt: UiBoxOpt & { + children?: any; +}): FunctionalComponent; +interface UiImageOpt extends UiRenderableOpt { + image?: string; + imageOpacity?: number | string; +} +export declare function Image(opt: UiImageOpt & { + children?: any; +}): FunctionalComponent; +interface UiInputOpt extends UiTextOpt { + placeholder?: string; + placeholderColor?: string; + placeholderOpacity?: number | string; + focus?: string | boolean; + onInput?: (e: AreactEvent) => void; +} +export declare function Input(opt: UiInputOpt & { + children?: any; +}): FunctionalComponent; +export {}; diff --git a/dts/areact/index.d.ts b/dts/areact/index.d.ts new file mode 100644 index 0000000..4c0c193 --- /dev/null +++ b/dts/areact/index.d.ts @@ -0,0 +1,33 @@ +import { h } from "preact"; +export declare const html: (strings: TemplateStringsArray, ...values: any[]) => import("preact").VNode | import("preact").VNode[]; +export declare const document: any, HTMLElement: any, Event: any; +export declare class AreactApp { + root: HTMLElement; + uiNode: UiNode; + __interval: any; + static h: typeof h; + static frag: import("preact").FunctionComponent<{}>; + static supportedNodes: string[]; + uiGenedProps: Record; + inputNodes: any[]; + renderedNodes: any[]; + constructor(); + static initUiComponents(): Record; + mount(ele: any, uiNode: UiNode): void; + render(): void; + _bindProps(node: Element | HTMLElement, uiNode: UiRenderable | UiNode, string_props_name: any, special_converters: any): void; + static _uiPropsGen(propTypes: Record): { + string_props_name: any; + special_converters: any; + }; + static getConvertorForType(type: string): (val: string) => any | undefined; + static _camelCaseToDash(str: string): string; + static _dashToCamelCase(str: string): string; + createNodeInUi(node: Element | HTMLElement): UiText | UiBox | UiInput | UiImage | UiRenderable; + renderNodes(node: Element, uiNode: UiRenderable | UiNode): void; +} +export declare const Areact: typeof AreactApp; +export * as hooks from "preact/hooks"; diff --git a/package.json b/package.json index c629bdc..b1b7a6b 100644 --- a/package.json +++ b/package.json @@ -15,18 +15,25 @@ }, "dependencies": { "@docusaurus/core": "^3.5.2", + "@docusaurus/plugin-vercel-analytics": "^3.5.2", "@docusaurus/preset-classic": "^3.5.2", "@easyops-cn/docusaurus-search-local": "^0.44.5", "@mdx-js/react": "^3.0.0", "@node-rs/jieba": "^1.10.3", "clsx": "^2.0.0", + "docusaurus-remark-plugin-tab-blocks": "^3.1.0", "prism-react-renderer": "^2.3.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.5.2", - "@docusaurus/types": "^3.5.2" + "@docusaurus/types": "^3.5.2", + "docusaurus-plugin-react-docgen-typescript": "^1.2.1", + "docusaurus-plugin-typedoc": "^1.0.5", + "react-docgen-typescript": "^2.2.2", + "typedoc": "^0.26.7", + "typedoc-plugin-markdown": "^4.2.7" }, "browserslist": { "production": [ diff --git a/sidebars.js b/sidebars.js index 3327580..93b3585 100644 --- a/sidebars.js +++ b/sidebars.js @@ -14,8 +14,9 @@ /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], - + tutorialSidebar: [ + {type: 'autogenerated', dirName: '.'}, + ], // But you can create a sidebar manually /* tutorialSidebar: [ diff --git a/src/css/custom.css b/src/css/custom.css index 2bc6a4c..79477ac 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -5,7 +5,7 @@ */ /* You can override the default Infima variables here. */ -:root { +/* :root { --ifm-color-primary: #2e8555; --ifm-color-primary-dark: #29784c; --ifm-color-primary-darker: #277148; @@ -15,10 +15,23 @@ --ifm-color-primary-lightest: #3cad6e; --ifm-code-font-size: 95%; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} */ +:root { + --ifm-color-primary: #673ab7; + --ifm-color-primary-dark: #5d34a5; + --ifm-color-primary-darker: #58319c; + --ifm-color-primary-darkest: #482980; + --ifm-color-primary-light: #7345c4; + --ifm-color-primary-lighter: #7a4ec7; + --ifm-color-primary-lightest: #341d5b; +} +:root{ + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); } /* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { +/* [data-theme='dark'] { --ifm-color-primary: #25c2a0; --ifm-color-primary-dark: #21af90; --ifm-color-primary-darker: #1fa588; @@ -27,4 +40,16 @@ --ifm-color-primary-lighter: #32d8b4; --ifm-color-primary-lightest: #4fddbf; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} */ +[data-theme='dark'] { + --ifm-color-primary: #9575cd; + --ifm-color-primary-dark: #9373cc; + --ifm-color-primary-darker: #7a52c0; + --ifm-color-primary-darkest: #623ca5; + --ifm-color-primary-light: #a78dd6; + --ifm-color-primary-lighter: #b098da; + --ifm-color-primary-lightest: #cbbce7; } +[data-theme='dark'] { + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} \ No newline at end of file