u
This commit is contained in:
1 parent
17e07df91a
commit
3ef263f5cc
47 files changed
+1169
-1041
No files matched your search
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
1
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"label": "Tutorial - Extras",
|
||||
"label": "编程",
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
@@ -0,0 +1,9 @@
|
||||
[React Native](https://reactnative.dev/) 是一个开源框架,用于构建跨平台的移动应用。它允许开发者使用 React 来构建原生的 iOS 和 Android 应用。
|
||||
|
||||
主要特点:
|
||||
- 跨平台开发:通过一次编写代码,React Native 可以在 iOS 和 Android 上运行,大大减少开发时间和成本。
|
||||
- 原生性能:React Native 使用原生组件(如按钮、文本框等),提供接近原生应用的性能和用户体验。
|
||||
- 热重载:开发者可以实时查看代码修改的效果,而无需重新加载整个应用。
|
||||
- 组件化开发:通过 React 的组件化理念,开发者可以更高效地构建和维护应用界面。
|
||||
- 丰富的生态系统:React Native 拥有大量的第三方库和工具,帮助开发者快速实现功能。
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[Expo](https://docs.expo.dev/) 是一个基于 React Native 的框架,旨在简化移动应用开发。它提供了一套工具和服务,让开发者可以更快速地构建、测试和发布 React Native 应用。
|
||||
|
||||
主要特点:
|
||||
- 快速上手:无需复杂配置,直接在手机上查看代码效果。
|
||||
- 内置功能:相机、位置服务、推送通知等常用功能,免去手动集成。
|
||||
- 跨平台支持:同一套代码可在 iOS 和 Android 上运行。
|
||||
- 热重载:修改代码后,应用会即时更新,提升开发效率。
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
创建项目命令:
|
||||
|
||||
```bash
|
||||
pnpm create expo-app@latest --template blank-typescript
|
||||
```
|
||||
|
||||
[官网模板](https://docs.expo.dev/more/create-expo/) 如下:
|
||||
|
||||
| 模板名称 | 描述 |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| [`default`](https://github.com/expo/expo/tree/main/templates/expo-template-default) | 默认模板,用于构建多页面应用。包含推荐工具,如 **Expo CLI**、**Expo Router** 路由库,并启用 **TypeScript** 配置。适合大多数应用。 |
|
||||
| [`blank`](https://github.com/expo/expo/tree/main/templates/expo-template-blank) | 最简模板,仅安装最基本依赖,不包含导航配置。 |
|
||||
| [`blank-typescript`](https://github.com/expo/expo/tree/main/templates/expo-template-blank-typescript) | 空白模板,但启用了 **TypeScript** 支持。 |
|
||||
| [`tabs`](https://github.com/expo/expo/tree/main/templates/expo-template-tabs) | 启用基于文件的路由系统(Expo Router),并默认使用 **TypeScript**,适合多标签页结构的应用。 |
|
||||
| [`bare-minimum`](https://github.com/expo/expo/tree/main/templates/expo-template-bare-minimum) | 带有原生目录(`android` 与 `ios`)的空白模板,创建时会自动运行 [`npx expo prebuild`](https://docs.expo.dev/workflow/prebuild)。适合需要直接修改原生代码的项目。 |
|
||||
|
||||
**启动项目:**
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
**生成基础文件**,后面要用到:
|
||||
|
||||
```bash
|
||||
npx expo customize babel.config.js
|
||||
npx expo customize metro.config.js
|
||||
```
|
||||
|
||||
## 配置eslint格式化
|
||||
|
||||
可以使用快捷命令(推荐):
|
||||
|
||||
```bash
|
||||
pnpm dlx @antfu/eslint-config@latest
|
||||
```
|
||||
|
||||
安装:
|
||||
|
||||
```bash
|
||||
pnpm i -D eslint @antfu/eslint-config
|
||||
```
|
||||
|
||||
新建 `eslint.config.mjs` 文件:
|
||||
|
||||
```js
|
||||
import antfu from '@antfu/eslint-config'
|
||||
|
||||
export default antfu({
|
||||
stylistic: {
|
||||
indent: 2, // 缩进为 2 个空格(可选 4 或 'tab')
|
||||
quotes: 'single', // 使用单引号(可选 'double')
|
||||
},
|
||||
rules: {
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/no-use-before-define': 'off', // 关闭 TS 的“定义前使用”警告
|
||||
'no-console': 'off', // 关闭 console 报错
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
配置 vscode 模板,修改 `.vscode/settings.json` :
|
||||
|
||||
> 注意安装 eslint 插件
|
||||
|
||||
```json
|
||||
{
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||
],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"xml",
|
||||
"gql",
|
||||
"graphql",
|
||||
"astro",
|
||||
"svelte",
|
||||
"css",
|
||||
"less",
|
||||
"scss",
|
||||
"pcss",
|
||||
"postcss"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 配置深度链接
|
||||
|
||||
作用:通过链接就可以跳转到该app。
|
||||
|
||||
在 `app.json` 文件下进行配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"expo": {
|
||||
"scheme": "oneao"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后就可以通过这个配置 从别的应用或者浏览器跳转到你的app:
|
||||
|
||||
```
|
||||
oneao://courses/react-native
|
||||
```
|
||||
|
||||
## 定义 src 路径别名 @
|
||||
|
||||
> 参考:https://docs.expo.dev/guides/typescript/
|
||||
|
||||
修改 `tsconfig.json` 文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"extends": "expo/tsconfig.base",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
安装 `module-resolver` :
|
||||
|
||||
```
|
||||
pnpm add -D babel-plugin-module-resolver
|
||||
```
|
||||
|
||||
修改 `babel.config.js` :
|
||||
|
||||
```js
|
||||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
return {
|
||||
presets: [
|
||||
['babel-preset-expo', { jsxImportSource: 'nativewind' }],
|
||||
'nativewind/babel',
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
root: ['./src'],
|
||||
alias: {
|
||||
'@': './src',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,189 @@
|
||||
## 云打包
|
||||
1. 安装 EAS CLI
|
||||
|
||||
```bash
|
||||
npm install -g eas-cli
|
||||
```
|
||||
|
||||
2. 注册并登录 Expo 账户
|
||||
|
||||
- 访问 https://expo.dev/ 注册账户,然后命令行登录:
|
||||
|
||||
```bash
|
||||
expo login
|
||||
```
|
||||
|
||||
3. 配置项目构建
|
||||
|
||||
- 输入以下命令,选择 Android 平台即可。
|
||||
|
||||
```bash
|
||||
eas build:configure
|
||||
```
|
||||
|
||||
4. 构建 Android 包
|
||||
|
||||
- 默认构建 AAB 包(上传 Google Play):
|
||||
|
||||
```bash
|
||||
eas build --platform android
|
||||
```
|
||||
|
||||
- 构建 APK(可直接安装):
|
||||
|
||||
```bash
|
||||
eas build -p android --profile preview
|
||||
```
|
||||
|
||||
## 本地打包(EAS方式)
|
||||
|
||||
## 本地打包(原生方式)
|
||||
### 1. 安装 [Android Studio]( https://developer.android.com/?hl=zh-cn)
|
||||
|
||||
1. 下载安装
|
||||
|
||||
- 按照官网提示依次安装 SDK 工具和必要组件。
|
||||
|
||||
- 安装完成后,记住 SDK 的安装路径(例如:`D:\data\sdk`,默认位置是:`C:\Users\<你的用户名>\AppData\Local\Android\Sdk`),后续需要用到。
|
||||
|
||||
2. 配置环境变量
|
||||
|
||||
- 打开 **系统属性 → 高级 → 环境变量**。
|
||||
|
||||
- 在 **系统变量** 中新增:
|
||||
|
||||
```
|
||||
变量名:ANDROID_HOME
|
||||
变量值:D:\data\sdk
|
||||
```
|
||||
|
||||
- 在 **Path** 中新增:
|
||||
|
||||
```bash
|
||||
%ANDROID_HOME%\platform-tools
|
||||
%ANDROID_HOME%\tools
|
||||
%ANDROID_HOME%\tools\bin
|
||||
```
|
||||
|
||||
- 保存设置。
|
||||
|
||||
3. 验证安装
|
||||
|
||||
- 打开命令行,执行:
|
||||
|
||||
```
|
||||
echo %ANDROID_HOME%
|
||||
```
|
||||
|
||||
- 如果输出 SDK 路径,则说明配置成功。
|
||||
|
||||
|
||||
|
||||
### 2. 安装 [jdk17](https://adoptium.net/zh-CN/temurin/releases?version=8&os=any&arch=any)
|
||||
|
||||
1. 下载安装
|
||||
|
||||
- 下载对应版本解压即可。
|
||||
- 安装成功后,记住 jdk 的安装路径,后续需要用到。
|
||||
|
||||
2. 配置环境变量
|
||||
|
||||
- 打开 **系统属性 → 高级 → 环境变量**。
|
||||
|
||||
- 在 **系统变量** 中新增:
|
||||
|
||||
```bash
|
||||
变量名:JAVA_HOME
|
||||
变量值:D:\devtool\jdk\jdk-17.0.16+8
|
||||
```
|
||||
|
||||
- 在 **Path** 中新增:
|
||||
|
||||
```bash
|
||||
%JAVA_HOME%\bin
|
||||
```
|
||||
|
||||
- 保存设置。
|
||||
|
||||
3. 验证安装
|
||||
|
||||
- 打开命令行,执行:
|
||||
|
||||
```bash
|
||||
java --version
|
||||
```
|
||||
|
||||
- 如果输出 jdk 的信息,则说明配置成功。
|
||||
|
||||
|
||||
|
||||
### 3. Expo 项目启用 [Prebuild](https://docs.expo.dev/workflow/prebuild/)
|
||||
|
||||
1. 生成原生项目
|
||||
|
||||
```bash
|
||||
npx expo prebuild
|
||||
```
|
||||
|
||||
然后执行完毕后,会生成 `android` 目录。
|
||||
|
||||
2. 清除并重新生成原生项目
|
||||
|
||||
```bash
|
||||
npx expo prebuild --clean
|
||||
```
|
||||
|
||||
会删除已有的 `android` 和 `ios` 目录,然后重新生成。
|
||||
|
||||
## 打包体积优化
|
||||
方法一:找到 `android/gradle.properties` 文件,找到 `reactNativeArchitectures` ,根据需要删除即可
|
||||
|
||||
```properties
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
```
|
||||
|
||||
方法二:原生方法,在 `android/app/build.gradle` 文件中添加或修改以下配置:
|
||||
|
||||
```
|
||||
android {
|
||||
// 其他配置...
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true // 启用按 CPU 架构拆分
|
||||
reset() // 重置默认架构列表
|
||||
include 'arm64-v8a', 'armeabi-v7a'
|
||||
universalApk false // 是否生成包含所有架构的通用 APK(false 表示只生成分架构的独立包)
|
||||
// 还有一个 exclude 配置是排除指定架构的
|
||||
}
|
||||
}
|
||||
|
||||
// 其他配置...
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
|
||||
- `enable true`:启用架构拆分功能。
|
||||
- `reset()`:清空默认的架构配置,避免重复。
|
||||
- `include`:指定要生成的架构包,比如只打 `arm64-v8a` 和 `armeabi-v7a`。
|
||||
- `universalApk false`:不生成通用包(即包含所有架构的 APK)。
|
||||
|
||||
**举例:**
|
||||
|
||||
执行打包命令后,将生成多个包,例如:
|
||||
|
||||
```
|
||||
app-arm64-v8a-release.apk
|
||||
app-armeabi-v7a-release.apk
|
||||
```
|
||||
|
||||
每个 APK 只包含对应架构的二进制文件,体积更小。
|
||||
|
||||
如果把 `universalApk` 设置为 `true`,则只会生成一个:
|
||||
|
||||
```
|
||||
app-universal-release.apk
|
||||
```
|
||||
|
||||
这个包能在 `arm64-v8a` 和 `armeabi-v7a` 架构的设备上运行,但体积最大。
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "expo",
|
||||
"position": 1,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
NativeWind 原子化 CSS 库
|
||||
> 官网:https://www.nativewind.dev/docs/getting-started/installation
|
||||
|
||||
1. 安装依赖
|
||||
|
||||
```bash
|
||||
npx expo add nativewind react-native-reanimated react-native-safe-area-context
|
||||
npx expo add tailwindcss@^3.4.17 -- --save-dev
|
||||
```
|
||||
|
||||
2. 运行 `npx tailwindcss init` 生成 `tailwind.config.js` 文件,添加配置:
|
||||
|
||||
```js
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
// NOTE: Update this to include the paths to all files that contain Nativewind classes.
|
||||
content: ['./App.tsx', './components/**/*.{js,jsx,ts,tsx}'],
|
||||
presets: [require('nativewind/preset')],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
|
||||
3. 创建 `global.css` 文件:
|
||||
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
```
|
||||
|
||||
|
||||
注意在全局引入
|
||||
|
||||
4. 修改 `babel.config.js` 文件:
|
||||
|
||||
> 如果没有该文件,执行 `npx expo customize babel.config.js` 命令创建。
|
||||
|
||||
```js
|
||||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
return {
|
||||
presets: [
|
||||
['babel-preset-expo', { jsxImportSource: 'nativewind' }],
|
||||
'nativewind/babel',
|
||||
],
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. 修改 `metro.config.js` 文件:
|
||||
|
||||
> 如果没有该文件,执行 `npx expo customize metro.config.js` 命令创建。
|
||||
|
||||
```js
|
||||
const { getDefaultConfig } = require('expo/metro-config')
|
||||
const { withNativeWind } = require('nativewind/metro')
|
||||
|
||||
const config = getDefaultConfig(__dirname)
|
||||
|
||||
module.exports = withNativeWind(config, { input: './global.css' })
|
||||
```
|
||||
|
||||
6. 新建 `src/types/nativewind-env.d.ts` 文件:
|
||||
|
||||
```ts
|
||||
/// <reference types="nativewind/types" />
|
||||
```
|
||||
|
||||
7. 安装 ` react-native-css-interop` :
|
||||
|
||||
```bash
|
||||
npx expo install react-native-css-interop
|
||||
```
|
||||
|
||||
8. 检验是否生效
|
||||
|
||||
```tsx
|
||||
import { Text, View } from 'react-native'
|
||||
import './global.css'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<View className="flex-1 items-center justify-center bg-white">
|
||||
<Text className="text-xl font-bold text-blue-500">
|
||||
Welcome to Nativewind!
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
如果不生效,记得修改 `tailwind.config.js` 的目标路径:
|
||||
|
||||
```js
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
// NOTE: Update this to include the paths to all files that contain Nativewind classes.
|
||||
content: ['./src/app/_layout.tsx', './src/components/**/*.{js,jsx,ts,tsx}'],
|
||||
presets: [require('nativewind/preset')],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,290 @@
|
||||
Gluestack UI UI 组件库
|
||||
> 官网:https://gluestack.io/ui/docs/home/getting-started/installation
|
||||
|
||||
## 方式一:CLI
|
||||
|
||||
```bash
|
||||
npx gluestack-ui init
|
||||
```
|
||||
|
||||
## 方式二:Manual(推荐)
|
||||
|
||||
**前置条件:** 需要安装 `nativewind`
|
||||
|
||||
1. 安装依赖:
|
||||
|
||||
```bash
|
||||
npx expo add @gluestack-ui/core @gluestack-ui/utils react-native-svg @gluestack/ui-next-adapter
|
||||
```
|
||||
|
||||
2. 从 [github](https://github.com/gluestack/gluestack-ui/tree/main/src/components/ui/gluestack-ui-provider) 复制必须的组件到 `@/components/ui/gluestack-ui-provider`。
|
||||
|
||||
3. 修改 `tailwind.config.js` 文件:
|
||||
|
||||
```bash
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : 'class',
|
||||
content: [
|
||||
'./app/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./components/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./utils/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./*.{html,js,jsx,ts,tsx,mdx}',
|
||||
'./src/**/*.{html,js,jsx,ts,tsx,mdx}',
|
||||
],
|
||||
presets: [require('nativewind/preset')],
|
||||
important: 'html',
|
||||
safelist: [
|
||||
{
|
||||
pattern:
|
||||
// eslint-disable-next-line regexp/no-dupe-disjunctions
|
||||
/(bg|border|text|stroke|fill)-(primary|secondary|tertiary|error|success|warning|info|typography|outline|background|indicator)-(0|50|100|200|300|400|500|600|700|800|900|950|white|gray|black|error|warning|muted|success|info|light|dark|primary)/,
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
0: 'rgb(var(--color-primary-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-primary-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-primary-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-primary-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-primary-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-primary-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-primary-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-primary-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-primary-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-primary-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-primary-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-primary-950)/<alpha-value>)',
|
||||
},
|
||||
secondary: {
|
||||
0: 'rgb(var(--color-secondary-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-secondary-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-secondary-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-secondary-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-secondary-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-secondary-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-secondary-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-secondary-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-secondary-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-secondary-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-secondary-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-secondary-950)/<alpha-value>)',
|
||||
},
|
||||
tertiary: {
|
||||
50: 'rgb(var(--color-tertiary-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-tertiary-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-tertiary-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-tertiary-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-tertiary-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-tertiary-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-tertiary-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-tertiary-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-tertiary-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-tertiary-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-tertiary-950)/<alpha-value>)',
|
||||
},
|
||||
error: {
|
||||
0: 'rgb(var(--color-error-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-error-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-error-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-error-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-error-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-error-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-error-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-error-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-error-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-error-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-error-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-error-950)/<alpha-value>)',
|
||||
},
|
||||
success: {
|
||||
0: 'rgb(var(--color-success-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-success-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-success-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-success-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-success-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-success-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-success-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-success-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-success-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-success-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-success-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-success-950)/<alpha-value>)',
|
||||
},
|
||||
warning: {
|
||||
0: 'rgb(var(--color-warning-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-warning-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-warning-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-warning-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-warning-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-warning-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-warning-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-warning-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-warning-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-warning-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-warning-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-warning-950)/<alpha-value>)',
|
||||
},
|
||||
info: {
|
||||
0: 'rgb(var(--color-info-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-info-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-info-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-info-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-info-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-info-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-info-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-info-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-info-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-info-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-info-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-info-950)/<alpha-value>)',
|
||||
},
|
||||
typography: {
|
||||
0: 'rgb(var(--color-typography-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-typography-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-typography-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-typography-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-typography-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-typography-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-typography-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-typography-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-typography-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-typography-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-typography-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-typography-950)/<alpha-value>)',
|
||||
white: '#FFFFFF',
|
||||
gray: '#D4D4D4',
|
||||
black: '#181718',
|
||||
},
|
||||
outline: {
|
||||
0: 'rgb(var(--color-outline-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-outline-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-outline-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-outline-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-outline-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-outline-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-outline-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-outline-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-outline-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-outline-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-outline-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-outline-950)/<alpha-value>)',
|
||||
},
|
||||
background: {
|
||||
0: 'rgb(var(--color-background-0)/<alpha-value>)',
|
||||
50: 'rgb(var(--color-background-50)/<alpha-value>)',
|
||||
100: 'rgb(var(--color-background-100)/<alpha-value>)',
|
||||
200: 'rgb(var(--color-background-200)/<alpha-value>)',
|
||||
300: 'rgb(var(--color-background-300)/<alpha-value>)',
|
||||
400: 'rgb(var(--color-background-400)/<alpha-value>)',
|
||||
500: 'rgb(var(--color-background-500)/<alpha-value>)',
|
||||
600: 'rgb(var(--color-background-600)/<alpha-value>)',
|
||||
700: 'rgb(var(--color-background-700)/<alpha-value>)',
|
||||
800: 'rgb(var(--color-background-800)/<alpha-value>)',
|
||||
900: 'rgb(var(--color-background-900)/<alpha-value>)',
|
||||
950: 'rgb(var(--color-background-950)/<alpha-value>)',
|
||||
error: 'rgb(var(--color-background-error)/<alpha-value>)',
|
||||
warning: 'rgb(var(--color-background-warning)/<alpha-value>)',
|
||||
muted: 'rgb(var(--color-background-muted)/<alpha-value>)',
|
||||
success: 'rgb(var(--color-background-success)/<alpha-value>)',
|
||||
info: 'rgb(var(--color-background-info)/<alpha-value>)',
|
||||
light: '#FBFBFB',
|
||||
dark: '#181719',
|
||||
},
|
||||
indicator: {
|
||||
primary: 'rgb(var(--color-indicator-primary)/<alpha-value>)',
|
||||
info: 'rgb(var(--color-indicator-info)/<alpha-value>)',
|
||||
error: 'rgb(var(--color-indicator-error)/<alpha-value>)',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
'heading': undefined,
|
||||
'body': undefined,
|
||||
'mono': undefined,
|
||||
'jakarta': ['var(--font-plus-jakarta-sans)'],
|
||||
'roboto': ['var(--font-roboto)'],
|
||||
'code': ['var(--font-source-code-pro)'],
|
||||
'inter': ['var(--font-inter)'],
|
||||
'space-mono': ['var(--font-space-mono)'],
|
||||
},
|
||||
fontWeight: {
|
||||
extrablack: '950',
|
||||
},
|
||||
fontSize: {
|
||||
'2xs': '10px',
|
||||
},
|
||||
boxShadow: {
|
||||
'hard-1': '-2px 2px 8px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-2': '0px 3px 10px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-3': '2px 2px 8px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-4': '0px -3px 10px 0px rgba(38, 38, 38, 0.20)',
|
||||
'hard-5': '0px 2px 10px 0px rgba(38, 38, 38, 0.10)',
|
||||
'soft-1': '0px 0px 10px rgba(38, 38, 38, 0.1)',
|
||||
'soft-2': '0px 0px 20px rgba(38, 38, 38, 0.2)',
|
||||
'soft-3': '0px 0px 30px rgba(38, 38, 38, 0.1)',
|
||||
'soft-4': '0px 0px 40px rgba(38, 38, 38, 0.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
4. 使用 `GluestackUIProvider` 包括着整个组件:
|
||||
|
||||
```tsx
|
||||
// App.tsx
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import '@/global.css' // Make sure this path is correct
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<GluestackUIProvider>{/* Add your app code here */}</GluestackUIProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
下载对应组件:
|
||||
|
||||
```bash
|
||||
npx gluestack-ui add button
|
||||
```
|
||||
|
||||
引入使用:
|
||||
|
||||
```tsx
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { Button, ButtonText } from '@/components/ui/button'
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import '@/styles/global.css'
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<GluestackUIProvider>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View className="flex-1 items-center justify-center bg-white">
|
||||
<Text className="text-xl font-bold text-blue-500">
|
||||
Welcome to Nativewind!
|
||||
</Text>
|
||||
<Button>
|
||||
<ButtonText>你好</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</GluestackUIProvider>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f5f5f5',
|
||||
paddingHorizontal: 16,
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
Lucide 是一个轻量级、开源的图标库
|
||||
> 官网:https://lucide.dev/icons/
|
||||
|
||||
```bash
|
||||
npx expo add lucide-react-native
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "第三方库",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
Docusaurus 是一个开源的静态网站生成器,专注于帮助开发者快速构建、部署和维护文档类网站。
|
||||
> 官网:https://docusaurus.io/zh-CN/
|
||||
|
||||
## 优点
|
||||
- 高度自定义:
|
||||
- 使用 react 开发,并支持自定义扩展组件或自定义页面
|
||||
- 提供 swizzle 命令可完全自定义布局
|
||||
- 基础页面干净整洁,可快速捕捉到关键信息
|
||||
- 社区群庞大,拥有广大的社区支持
|
||||
- 同时支持 docs(文档) 和 blog(博客),能够有更多的选择
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1.环境要求
|
||||
- Node版本最低 20
|
||||
|
||||
### 2.安装
|
||||
```bash
|
||||
npx create-docusaurus@latest my-website classic
|
||||
```
|
||||
如果需要支持 `typescript`,则使用
|
||||
```bash
|
||||
npx create-docusaurus@latest my-website classic --typescript
|
||||
```
|
||||
|
||||
### 3.运行
|
||||
下载依赖
|
||||
```bash
|
||||
pnpm i
|
||||
```
|
||||
运行项目
|
||||
```bash
|
||||
pnpm run start
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
默认的项目结构如下
|
||||
```text
|
||||
my-website
|
||||
├── blog
|
||||
│ ├── 2019-05-28-hola.md
|
||||
│ ├── 2019-05-29-hello-world.md
|
||||
│ └── 2020-05-30-welcome.md
|
||||
├── docs
|
||||
│ ├── doc1.md
|
||||
│ ├── doc2.md
|
||||
│ ├── doc3.md
|
||||
│ └── mdx.md
|
||||
├── src
|
||||
│ ├── css
|
||||
│ │ └── custom.css
|
||||
│ └── pages
|
||||
│ ├── styles.module.css
|
||||
│ └── index.ts
|
||||
├── static
|
||||
│ └── img
|
||||
├── docusaurus.config.ts
|
||||
├── package.json
|
||||
├── README.md
|
||||
├── sidebars.ts
|
||||
└── yarn.lock
|
||||
```
|
||||
|
||||
目录介绍:
|
||||
|
||||
- `blog` - 存放博客文章,格式是 Markdown
|
||||
- `docs` - 存放文档内容,也用 Markdown 格式
|
||||
- `src` - 存放自定义的页面和样式。
|
||||
- `static` - 存放自定义的页面和样式
|
||||
- `docusaurus.config.ts` - 配置网站的各种信息
|
||||
- `sidebars.ts` - 配置 docs 侧边栏的内容和结构
|
||||
@@ -0,0 +1,123 @@
|
||||
关于文档的创建和配置
|
||||
|
||||
## 1. navbar 配置
|
||||
|
||||
在 `docusaurus.config.ts` 文件中进行配置, `config` - `themeConfig` - `navbar` - `items`
|
||||
|
||||
### 1.1 单级导航
|
||||
|
||||
在 `docs` 文件夹下创建 `test.md` 文件,并在 `items` 配置项中添加:
|
||||
|
||||
```json
|
||||
{
|
||||
// ...
|
||||
"items": [
|
||||
{
|
||||
"to": "/docs/test",
|
||||
"label": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
刷新页面,即可进行预览
|
||||
|
||||
### 1.2 多级导航
|
||||
|
||||
docusaurus 的 navbar 最多支持两级导航,比如:
|
||||
|
||||
```json
|
||||
{
|
||||
// ...
|
||||
"items": [
|
||||
{
|
||||
"type": "dropdown",
|
||||
"label": "编程语言",
|
||||
"position": "left",
|
||||
"items": [
|
||||
{
|
||||
"label": "Stack Overflow",
|
||||
"href": "https://stackoverflow.com/questions/tagged/docusaurus"
|
||||
},
|
||||
{
|
||||
"label": "Discord",
|
||||
"href": "https://discordapp.com/invite/docusaurus"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 侧边栏配置
|
||||
|
||||
docs 的侧边栏配置主要在 `sidebars.ts` 文件中
|
||||
|
||||
### 2.1 自动生成
|
||||
|
||||
在 docs 目录中创建 `coding/java` 目录,在 `sidebar.ts` 文件中添加配置:
|
||||
|
||||
```ts
|
||||
import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'
|
||||
|
||||
const sidebars: SidebarsConfig = {
|
||||
// coding 目录下的自动生成的 Java 相关文档
|
||||
javaSidebar: [{ type: 'autogenerated', dirName: 'coding/java' }]
|
||||
}
|
||||
|
||||
export default sidebars
|
||||
```
|
||||
|
||||
修改 `docusaurus.config.ts` 文件:
|
||||
|
||||
```json
|
||||
{
|
||||
// ...
|
||||
"items": [
|
||||
{
|
||||
"type": "dropdown",
|
||||
"label": "编程语言",
|
||||
"position": "left",
|
||||
"items": [
|
||||
{
|
||||
"type": "docSidebar",
|
||||
"label": "Java",
|
||||
"sidebarId": "javaSidebar"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
提示:
|
||||
|
||||
- `sidebarId` 需要和 `SidebarsConfig` 中的 key 保持一致
|
||||
- `type: 'autogenerated'` 表示自动生成
|
||||
- **自动生成模式下 将自动根据目录和目录下的文件自动构建侧边栏**
|
||||
|
||||
### 2.2 手动配置
|
||||
|
||||
```ts
|
||||
import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'
|
||||
|
||||
const sidebars: SidebarsConfig = {
|
||||
// 手动配置的侧边栏
|
||||
javaSidebar: [
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Java 基础',
|
||||
items: ['coding/java/intro', 'coding/java/advanced'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Java 框架',
|
||||
items: ['coding/java/spring-boot', 'coding/java/hibernate'],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export default sidebars
|
||||
```
|
||||
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,119 @@
|
||||
> 具体可参考:https://docusaurus.io/zh-CN/docs/search
|
||||
|
||||
Algolia 是一个强大的搜索引擎平台,专注于提供快速、可定制的搜索功能。它通常用于为网站、应用或文档提供实时搜索和自动完成功能。
|
||||
|
||||
## 1. 获取 appId 和 apiKey
|
||||
|
||||
1. 先到 [官网](https://dashboard.algolia.com) 注册,大概两天后会收到邮件通知注册成功,再接着往下操作
|
||||
2. 登录
|
||||
3. 创建一个新的 Application,选择左边的免费版本进行下一步
|
||||
4. 定义 index_name,选择响应快的服务进行创建。到这一步就创建成功了
|
||||
5. 打开 API keys,获取三个需要的字段 Application ID、Search-Only API key、Admin API key
|
||||
|
||||
## 2. 修改 docusaurus.config.js
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"themeConfig": {
|
||||
"algolia": {
|
||||
"appId": "填写申请的appId",
|
||||
"apiKey": "填写申请的apiKey",
|
||||
"indexName": "填写申请的indexName"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这样就会自动在 `navbar` 添加个搜索框
|
||||
|
||||
## 3. 新增 `docsearch.json` 配置文件
|
||||
注意将信息换成自己的信息
|
||||
|
||||
```json
|
||||
{
|
||||
// 需要替换
|
||||
"index_name": "xxx",
|
||||
// 需要替换。网站网址
|
||||
"start_urls": ["xxx"],
|
||||
// 需要替换。sitemap的网址,docusaurus 默认在根目录下生成 sitemap.xml
|
||||
"sitemap_urls": ["xxx"],
|
||||
"selectors": {
|
||||
"lvl0": {
|
||||
"selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link menu__link--sublist menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]",
|
||||
"type": "xpath",
|
||||
"global": true,
|
||||
"default_value": "Documentation"
|
||||
},
|
||||
"lvl1": "header h1, article h1",
|
||||
"lvl2": "article h2",
|
||||
"lvl3": "article h3",
|
||||
"lvl4": "article h4",
|
||||
"lvl5": "article h5, article td:first-child",
|
||||
"lvl6": "article h6",
|
||||
"text": "article p, article li, article td:last-child"
|
||||
},
|
||||
"custom_settings": {
|
||||
"attributesForFaceting": [
|
||||
"type",
|
||||
"lang",
|
||||
"language",
|
||||
"version",
|
||||
"docusaurus_tag"
|
||||
],
|
||||
"attributesToRetrieve": [
|
||||
"hierarchy",
|
||||
"content",
|
||||
"anchor",
|
||||
"url",
|
||||
"url_without_anchor",
|
||||
"type"
|
||||
],
|
||||
"attributesToHighlight": ["hierarchy", "content"],
|
||||
"attributesToSnippet": ["content:10"],
|
||||
"camelCaseAttributes": ["hierarchy", "content"],
|
||||
"searchableAttributes": [
|
||||
"unordered(hierarchy.lvl0)",
|
||||
"unordered(hierarchy.lvl1)",
|
||||
"unordered(hierarchy.lvl2)",
|
||||
"unordered(hierarchy.lvl3)",
|
||||
"unordered(hierarchy.lvl4)",
|
||||
"unordered(hierarchy.lvl5)",
|
||||
"unordered(hierarchy.lvl6)",
|
||||
"content"
|
||||
],
|
||||
"distinct": true,
|
||||
"attributeForDistinct": "url",
|
||||
"customRanking": [
|
||||
"desc(weight.pageRank)",
|
||||
"desc(weight.level)",
|
||||
"asc(weight.position)"
|
||||
],
|
||||
"ranking": [
|
||||
"words",
|
||||
"filters",
|
||||
"typo",
|
||||
"attribute",
|
||||
"proximity",
|
||||
"exact",
|
||||
"custom"
|
||||
],
|
||||
"highlightPreTag": "<span class='algolia-docsearch-suggestion--highlight'>",
|
||||
"highlightPostTag": "</span>",
|
||||
"minWordSizefor1Typo": 3,
|
||||
"minWordSizefor2Typos": 7,
|
||||
"allowTyposOnNumericTokens": false,
|
||||
"minProximity": 1,
|
||||
"ignorePlurals": true,
|
||||
"advancedSyntax": true,
|
||||
"attributeCriteriaComputedByMinProximity": true,
|
||||
"removeWordsIfNoResults": "allOptional",
|
||||
"separatorsToIndex": "_",
|
||||
"synonyms": [
|
||||
["js", "javascript"],
|
||||
["ts", "typescript"]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 828 KiB |
@@ -1,47 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Tutorial Intro
|
||||
|
||||
Let's discover **Docusaurus in less than 5 minutes**.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Get started by **creating a new site**.
|
||||
|
||||
Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
|
||||
|
||||
### What you'll need
|
||||
|
||||
- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
|
||||
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
|
||||
|
||||
## Generate a new site
|
||||
|
||||
Generate a new Docusaurus site using the **classic template**.
|
||||
|
||||
The classic template will automatically be added to your project after you run the command:
|
||||
|
||||
```bash
|
||||
npm init docusaurus@latest my-website classic
|
||||
```
|
||||
|
||||
You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
|
||||
|
||||
The command also installs all necessary dependencies you need to run Docusaurus.
|
||||
|
||||
## Start your site
|
||||
|
||||
Run the development server:
|
||||
|
||||
```bash
|
||||
cd my-website
|
||||
npm run start
|
||||
```
|
||||
|
||||
The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
|
||||
|
||||
The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
|
||||
|
||||
Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.
|
||||
Whitespace-only changes.
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "Tutorial - Basics",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "5 minutes to learn the most important Docusaurus concepts."
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Congratulations!
|
||||
|
||||
You have just learned the **basics of Docusaurus** and made some changes to the **initial template**.
|
||||
|
||||
Docusaurus has **much more to offer**!
|
||||
|
||||
Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**.
|
||||
|
||||
Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)
|
||||
|
||||
## What's next?
|
||||
|
||||
- Read the [official documentation](https://docusaurus.io/)
|
||||
- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config)
|
||||
- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration)
|
||||
- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)
|
||||
- Add a [search bar](https://docusaurus.io/docs/search)
|
||||
- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)
|
||||
- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Create a Blog Post
|
||||
|
||||
Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...
|
||||
|
||||
## Create your first Post
|
||||
|
||||
Create a file at `blog/2021-02-28-greetings.md`:
|
||||
|
||||
```md title="blog/2021-02-28-greetings.md"
|
||||
---
|
||||
slug: greetings
|
||||
title: Greetings!
|
||||
authors:
|
||||
- name: Joel Marcey
|
||||
title: Co-creator of Docusaurus 1
|
||||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
- name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
tags: [greetings]
|
||||
---
|
||||
|
||||
Congratulations, you have made your first post!
|
||||
|
||||
Feel free to play around and edit this post as much as you like.
|
||||
```
|
||||
|
||||
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Create a Document
|
||||
|
||||
Documents are **groups of pages** connected through:
|
||||
|
||||
- a **sidebar**
|
||||
- **previous/next navigation**
|
||||
- **versioning**
|
||||
|
||||
## Create your first Doc
|
||||
|
||||
Create a Markdown file at `docs/hello.md`:
|
||||
|
||||
```md title="docs/hello.md"
|
||||
# Hello
|
||||
|
||||
This is my **first Docusaurus document**!
|
||||
```
|
||||
|
||||
A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).
|
||||
|
||||
## Configure the Sidebar
|
||||
|
||||
Docusaurus automatically **creates a sidebar** from the `docs` folder.
|
||||
|
||||
Add metadata to customize the sidebar label and position:
|
||||
|
||||
```md title="docs/hello.md" {1-4}
|
||||
---
|
||||
sidebar_label: "Hi!"
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
This is my **first Docusaurus document**!
|
||||
```
|
||||
|
||||
It is also possible to create your sidebar explicitly in `sidebars.js`:
|
||||
|
||||
```js title="sidebars.js"
|
||||
export default {
|
||||
tutorialSidebar: [
|
||||
"intro",
|
||||
// highlight-next-line
|
||||
"hello",
|
||||
{
|
||||
type: "category",
|
||||
label: "Tutorial",
|
||||
items: ["tutorial-basics/create-a-document"],
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Create a Page
|
||||
|
||||
Add **Markdown or React** files to `src/pages` to create a **standalone page**:
|
||||
|
||||
- `src/pages/index.js` → `localhost:3000/`
|
||||
- `src/pages/foo.md` → `localhost:3000/foo`
|
||||
- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`
|
||||
|
||||
## Create your first React Page
|
||||
|
||||
Create a file at `src/pages/my-react-page.js`:
|
||||
|
||||
```jsx title="src/pages/my-react-page.js"
|
||||
import React from "react";
|
||||
import Layout from "@theme/Layout";
|
||||
|
||||
export default function MyReactPage() {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>My React page</h1>
|
||||
<p>This is a React page</p>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).
|
||||
|
||||
## Create your first Markdown Page
|
||||
|
||||
Create a file at `src/pages/my-markdown-page.md`:
|
||||
|
||||
```mdx title="src/pages/my-markdown-page.md"
|
||||
# My Markdown page
|
||||
|
||||
This is a Markdown page
|
||||
```
|
||||
|
||||
A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Deploy your site
|
||||
|
||||
Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).
|
||||
|
||||
It builds your site as simple **static HTML, JavaScript and CSS files**.
|
||||
|
||||
## Build your site
|
||||
|
||||
Build your site **for production**:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The static files are generated in the `build` folder.
|
||||
|
||||
## Deploy your site
|
||||
|
||||
Test your production build locally:
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).
|
||||
|
||||
You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).
|
||||
@@ -1,153 +0,0 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Markdown Features
|
||||
|
||||
Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.
|
||||
|
||||
## Front Matter
|
||||
|
||||
Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):
|
||||
|
||||
```text title="my-doc.md"
|
||||
// highlight-start
|
||||
---
|
||||
id: my-doc-id
|
||||
title: My document title
|
||||
description: My document description
|
||||
slug: /my-custom-url
|
||||
---
|
||||
// highlight-end
|
||||
|
||||
## Markdown heading
|
||||
|
||||
Markdown text with [links](./hello.md)
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
Regular Markdown links are supported, using url paths or relative file paths.
|
||||
|
||||
```md
|
||||
Let's see how to [Create a page](/create-a-page).
|
||||
```
|
||||
|
||||
```md
|
||||
Let's see how to [Create a page](./create-a-page.md).
|
||||
```
|
||||
|
||||
**Result:** Let's see how to [Create a page](./create-a-page.md).
|
||||
|
||||
## Images
|
||||
|
||||
Regular Markdown images are supported.
|
||||
|
||||
You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`):
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them:
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
## Code Blocks
|
||||
|
||||
Markdown code blocks are supported with Syntax highlighting.
|
||||
|
||||
````md
|
||||
```jsx title="src/components/HelloDocusaurus.js"
|
||||
function HelloDocusaurus() {
|
||||
return <h1>Hello, Docusaurus!</h1>;
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```jsx title="src/components/HelloDocusaurus.js"
|
||||
function HelloDocusaurus() {
|
||||
return <h1>Hello, Docusaurus!</h1>;
|
||||
}
|
||||
```
|
||||
|
||||
## Admonitions
|
||||
|
||||
Docusaurus has a special syntax to create admonitions and callouts:
|
||||
|
||||
```md
|
||||
:::tip My tip
|
||||
|
||||
Use this awesome feature option
|
||||
|
||||
:::
|
||||
|
||||
:::danger Take care
|
||||
|
||||
This action is dangerous
|
||||
|
||||
:::
|
||||
```
|
||||
|
||||
:::tip My tip
|
||||
|
||||
Use this awesome feature option
|
||||
|
||||
:::
|
||||
|
||||
:::danger Take care
|
||||
|
||||
This action is dangerous
|
||||
|
||||
:::
|
||||
|
||||
## MDX and React Components
|
||||
|
||||
[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:
|
||||
|
||||
```jsx
|
||||
export const Highlight = ({children, color}) => (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderRadius: '20px',
|
||||
color: '#fff',
|
||||
padding: '10px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
alert(`You clicked the color ${color} with label ${children}`)
|
||||
}}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
|
||||
|
||||
This is <Highlight color="#1877F2">Facebook blue</Highlight> !
|
||||
```
|
||||
|
||||
export const Highlight = ({ children, color }) => (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderRadius: "20px",
|
||||
color: "#fff",
|
||||
padding: "10px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
alert(`You clicked the color ${color} with label ${children}`);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
|
||||
|
||||
This is <Highlight color="#1877F2">Facebook blue</Highlight> !
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
@@ -1,55 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Manage Docs Versions
|
||||
|
||||
Docusaurus can manage multiple versions of your docs.
|
||||
|
||||
## Create a docs version
|
||||
|
||||
Release a version 1.0 of your project:
|
||||
|
||||
```bash
|
||||
npm run docusaurus docs:version 1.0
|
||||
```
|
||||
|
||||
The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
|
||||
|
||||
Your docs now have 2 versions:
|
||||
|
||||
- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
|
||||
- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
|
||||
|
||||
## Add a Version Dropdown
|
||||
|
||||
To navigate seamlessly across versions, add a version dropdown.
|
||||
|
||||
Modify the `docusaurus.config.js` file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
items: [
|
||||
// highlight-start
|
||||
{
|
||||
type: "docsVersionDropdown",
|
||||
},
|
||||
// highlight-end
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The docs version dropdown appears in your navbar:
|
||||
|
||||

|
||||
|
||||
## Update an existing version
|
||||
|
||||
It is possible to edit versioned docs in their respective folder:
|
||||
|
||||
- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
|
||||
- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Translate your site
|
||||
|
||||
Let's translate `docs/intro.md` to French.
|
||||
|
||||
## Configure i18n
|
||||
|
||||
Modify `docusaurus.config.js` to add support for the `fr` locale:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: ["en", "fr"],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Translate a doc
|
||||
|
||||
Copy the `docs/intro.md` file to the `i18n/fr` folder:
|
||||
|
||||
```bash
|
||||
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
|
||||
|
||||
cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
|
||||
```
|
||||
|
||||
Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
|
||||
|
||||
## Start your localized site
|
||||
|
||||
Start your site on the French locale:
|
||||
|
||||
```bash
|
||||
npm run start -- --locale fr
|
||||
```
|
||||
|
||||
Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
|
||||
|
||||
:::caution
|
||||
|
||||
In development, you can only use one locale at a time.
|
||||
|
||||
:::
|
||||
|
||||
## Add a Locale Dropdown
|
||||
|
||||
To navigate seamlessly across languages, add a locale dropdown.
|
||||
|
||||
Modify the `docusaurus.config.js` file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
items: [
|
||||
// highlight-start
|
||||
{
|
||||
type: "localeDropdown",
|
||||
},
|
||||
// highlight-end
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The locale dropdown now appears in your navbar:
|
||||
|
||||

|
||||
|
||||
## Build your localized site
|
||||
|
||||
Build your site for a specific locale:
|
||||
|
||||
```bash
|
||||
npm run build -- --locale fr
|
||||
```
|
||||
|
||||
Or build your site to include all the locales at once:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
@@ -32,7 +32,7 @@ const config: Config = {
|
||||
// may want to replace "en" with "zh-Hans".
|
||||
i18n: {
|
||||
defaultLocale: 'zh-Hans',
|
||||
locales: ['zh-Hans', 'en'],
|
||||
locales: ['zh-Hans'],
|
||||
},
|
||||
presets: [
|
||||
[
|
||||
@@ -82,10 +82,38 @@ const config: Config = {
|
||||
hideOnScroll: true,
|
||||
items: [
|
||||
{
|
||||
type: 'docSidebar',
|
||||
sidebarId: 'tutorialSidebar',
|
||||
type: 'dropdown',
|
||||
label: '编程语言',
|
||||
position: 'left',
|
||||
label: '文档',
|
||||
items: [
|
||||
{
|
||||
type: 'docSidebar',
|
||||
label: 'Java',
|
||||
sidebarId: 'JavaSidebar',
|
||||
},
|
||||
{
|
||||
type: 'docSidebar',
|
||||
label: 'ReactNative',
|
||||
sidebarId: 'ReactNativeSidebar',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'dropdown',
|
||||
label: '开发工具',
|
||||
position: 'left',
|
||||
items: [
|
||||
{
|
||||
type: 'docSidebar',
|
||||
label: 'Docusaurus',
|
||||
sidebarId: 'DocusaurusSidebar',
|
||||
},
|
||||
{
|
||||
type: 'docSidebar',
|
||||
label: 'Git',
|
||||
sidebarId: 'GitSidebar',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
to: '/blog',
|
||||
|
||||
@@ -10,4 +10,7 @@ export default antfu({
|
||||
extends: [
|
||||
'plugin:@docusaurus/recommended',
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
},
|
||||
})
|
||||
+6
-26
@@ -1,33 +1,13 @@
|
||||
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
|
||||
|
||||
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
|
||||
|
||||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
const sidebars: SidebarsConfig = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
|
||||
// coding
|
||||
JavaSidebar: [{ type: "autogenerated", dirName: "coding/java" }],
|
||||
ReactNativeSidebar: [{ type: "autogenerated", dirName: "coding/react-native" }],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
'intro',
|
||||
'hello',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['tutorial-basics/create-a-document'],
|
||||
},
|
||||
],
|
||||
*/
|
||||
// dev-tools
|
||||
DocusaurusSidebar: [{ type: "autogenerated", dirName: "dev-tools/docusaurus" }],
|
||||
GitSidebar: [{ type: "autogenerated", dirName: "dev-tools/git" }],
|
||||
};
|
||||
|
||||
export default sidebars;
|
||||
@@ -18,9 +18,9 @@ function HomepageHeader() {
|
||||
</Heading>
|
||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||
<div className={styles.buttons}>
|
||||
<Link className="button button--secondary button--lg" to="/docs/intro">
|
||||
{/* <Link className="button button--secondary button--lg" to="/docs/intro">
|
||||
Docusaurus Tutorial - 5min ⏱️
|
||||
</Link>
|
||||
</Link> */}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import React, {useState, useRef, useEffect} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||
import NavbarItem from '@theme/NavbarItem';
|
||||
export default function DropdownNavbarItemDesktop({
|
||||
items,
|
||||
position,
|
||||
className,
|
||||
onClick,
|
||||
...props
|
||||
}) {
|
||||
const dropdownRef = useRef(null);
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (!dropdownRef.current || dropdownRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setShowDropdown(false);
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('touchstart', handleClickOutside);
|
||||
document.addEventListener('focusin', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchstart', handleClickOutside);
|
||||
document.removeEventListener('focusin', handleClickOutside);
|
||||
};
|
||||
}, [dropdownRef]);
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
||||
'dropdown--right': position === 'right',
|
||||
'dropdown--show': showDropdown,
|
||||
})}>
|
||||
<NavbarNavLink
|
||||
aria-haspopup="true"
|
||||
aria-expanded={showDropdown}
|
||||
role="button"
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
href={props.to ? undefined : '#'}
|
||||
className={clsx('navbar__link', className)}
|
||||
{...props}
|
||||
onClick={props.to ? undefined : (e) => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
setShowDropdown(!showDropdown);
|
||||
}
|
||||
}}>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<ul className="dropdown__menu">
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
isDropdownItem
|
||||
activeClassName="dropdown__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import type { Props } from '@theme/NavbarItem/DropdownNavbarItem/Desktop'
|
||||
import type { ReactNode } from 'react'
|
||||
import NavbarItem from '@theme/NavbarItem'
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'
|
||||
import clsx from 'clsx'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function DropdownNavbarItemDesktop({
|
||||
items,
|
||||
position,
|
||||
className,
|
||||
onClick,
|
||||
...props
|
||||
}: Props): ReactNode {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (
|
||||
event: MouseEvent | TouchEvent | FocusEvent,
|
||||
) => {
|
||||
if (
|
||||
!dropdownRef.current
|
||||
|| dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
return
|
||||
}
|
||||
setShowDropdown(false)
|
||||
}
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
document.addEventListener('touchstart', handleClickOutside)
|
||||
document.addEventListener('focusin', handleClickOutside)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
document.removeEventListener('touchstart', handleClickOutside)
|
||||
document.removeEventListener('focusin', handleClickOutside)
|
||||
}
|
||||
}, [dropdownRef])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
||||
'dropdown--right': position === 'right',
|
||||
'dropdown--show': showDropdown,
|
||||
})}
|
||||
>
|
||||
<NavbarNavLink
|
||||
aria-haspopup="true"
|
||||
aria-expanded={showDropdown}
|
||||
role="button"
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
href={props.to ? undefined : '#'}
|
||||
className={clsx('navbar__link', className)}
|
||||
{...props}
|
||||
onClick={props.to ? undefined : e => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
setShowDropdown(!showDropdown)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<ul className="dropdown__menu">
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
isDropdownItem
|
||||
activeClassName="dropdown__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
isRegexpStringMatch,
|
||||
useCollapsible,
|
||||
Collapsible,
|
||||
} from '@docusaurus/theme-common';
|
||||
import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||
import NavbarItem from '@theme/NavbarItem';
|
||||
import styles from './styles.module.css';
|
||||
function isItemActive(item, localPathname) {
|
||||
if (isSamePath(item.to, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function containsActiveItems(items, localPathname) {
|
||||
return items.some((item) => isItemActive(item, localPathname));
|
||||
}
|
||||
function CollapseButton({collapsed, onClick}) {
|
||||
return (
|
||||
<button
|
||||
aria-label={
|
||||
collapsed
|
||||
? translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.expandAriaLabel',
|
||||
message: 'Expand the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to expand the mobile dropdown navbar item',
|
||||
})
|
||||
: translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel',
|
||||
message: 'Collapse the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to collapse the mobile dropdown navbar item',
|
||||
})
|
||||
}
|
||||
aria-expanded={!collapsed}
|
||||
type="button"
|
||||
className="clean-btn menu__caret"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
function useItemCollapsible({active}) {
|
||||
const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
|
||||
initialState: () => !active,
|
||||
});
|
||||
// Expand if any item active after a navigation
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
setCollapsed(false);
|
||||
}
|
||||
}, [active, setCollapsed]);
|
||||
return {
|
||||
collapsed,
|
||||
toggleCollapsed,
|
||||
};
|
||||
}
|
||||
export default function DropdownNavbarItemMobile({
|
||||
items,
|
||||
className,
|
||||
position, // Need to destructure position from props so that it doesn't get passed on.
|
||||
onClick,
|
||||
...props
|
||||
}) {
|
||||
const localPathname = useLocalPathname();
|
||||
const isActive = isSamePath(props.to, localPathname);
|
||||
const containsActive = containsActiveItems(items, localPathname);
|
||||
const {collapsed, toggleCollapsed} = useItemCollapsible({
|
||||
active: isActive || containsActive,
|
||||
});
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
const href = props.to ? undefined : '#';
|
||||
return (
|
||||
<li
|
||||
className={clsx('menu__list-item', {
|
||||
'menu__list-item--collapsed': collapsed,
|
||||
})}>
|
||||
<div
|
||||
className={clsx('menu__list-item-collapsible', {
|
||||
'menu__list-item-collapsible--active': isActive,
|
||||
})}>
|
||||
<NavbarNavLink
|
||||
role="button"
|
||||
className={clsx(
|
||||
styles.dropdownNavbarItemMobile,
|
||||
'menu__link menu__link--sublist',
|
||||
className,
|
||||
)}
|
||||
href={href}
|
||||
{...props}
|
||||
onClick={(e) => {
|
||||
// Prevent navigation when link is "#"
|
||||
if (href === '#') {
|
||||
e.preventDefault();
|
||||
}
|
||||
// Otherwise we let navigation eventually happen, and/or collapse
|
||||
toggleCollapsed();
|
||||
}}>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<CollapseButton
|
||||
collapsed={collapsed}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
toggleCollapsed();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
mobile
|
||||
isDropdownItem
|
||||
onClick={onClick}
|
||||
activeClassName="menu__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</Collapsible>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
import React, {useEffect, type ReactNode, type ComponentProps} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
isRegexpStringMatch,
|
||||
useCollapsible,
|
||||
Collapsible,
|
||||
} from '@docusaurus/theme-common';
|
||||
import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||
import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem';
|
||||
import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Mobile';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function isItemActive(
|
||||
item: LinkLikeNavbarItemProps,
|
||||
localPathname: string,
|
||||
): boolean {
|
||||
if (isSamePath(item.to, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function containsActiveItems(
|
||||
items: readonly LinkLikeNavbarItemProps[],
|
||||
localPathname: string,
|
||||
): boolean {
|
||||
return items.some((item) => isItemActive(item, localPathname));
|
||||
}
|
||||
|
||||
function CollapseButton({
|
||||
collapsed,
|
||||
onClick,
|
||||
}: {
|
||||
collapsed: boolean;
|
||||
onClick: ComponentProps<'button'>['onClick'];
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
aria-label={
|
||||
collapsed
|
||||
? translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.expandAriaLabel',
|
||||
message: 'Expand the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to expand the mobile dropdown navbar item',
|
||||
})
|
||||
: translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel',
|
||||
message: 'Collapse the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to collapse the mobile dropdown navbar item',
|
||||
})
|
||||
}
|
||||
aria-expanded={!collapsed}
|
||||
type="button"
|
||||
className="clean-btn menu__caret"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function useItemCollapsible({active}: {active: boolean}) {
|
||||
const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
|
||||
initialState: () => !active,
|
||||
});
|
||||
|
||||
// Expand if any item active after a navigation
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
setCollapsed(false);
|
||||
}
|
||||
}, [active, setCollapsed]);
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
toggleCollapsed,
|
||||
};
|
||||
}
|
||||
|
||||
export default function DropdownNavbarItemMobile({
|
||||
items,
|
||||
className,
|
||||
position, // Need to destructure position from props so that it doesn't get passed on.
|
||||
onClick,
|
||||
...props
|
||||
}: Props): ReactNode {
|
||||
const localPathname = useLocalPathname();
|
||||
const isActive = isSamePath(props.to, localPathname);
|
||||
const containsActive = containsActiveItems(items, localPathname);
|
||||
|
||||
const {collapsed, toggleCollapsed} = useItemCollapsible({
|
||||
active: isActive || containsActive,
|
||||
});
|
||||
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
const href = props.to ? undefined : '#';
|
||||
|
||||
return (
|
||||
<li
|
||||
className={clsx('menu__list-item', {
|
||||
'menu__list-item--collapsed': collapsed,
|
||||
})}>
|
||||
<div
|
||||
className={clsx('menu__list-item-collapsible', {
|
||||
'menu__list-item-collapsible--active': isActive,
|
||||
})}>
|
||||
<NavbarNavLink
|
||||
role="button"
|
||||
className={clsx(
|
||||
styles.dropdownNavbarItemMobile,
|
||||
'menu__link menu__link--sublist',
|
||||
className,
|
||||
)}
|
||||
href={href}
|
||||
{...props}
|
||||
onClick={(e) => {
|
||||
// Prevent navigation when link is "#"
|
||||
if (href === '#') {
|
||||
e.preventDefault();
|
||||
}
|
||||
// Otherwise we let navigation eventually happen, and/or collapse
|
||||
toggleCollapsed();
|
||||
}}>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<CollapseButton
|
||||
collapsed={collapsed}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
toggleCollapsed();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
mobile
|
||||
isDropdownItem
|
||||
onClick={onClick}
|
||||
activeClassName="menu__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</Collapsible>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.dropdownNavbarItemMobile {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import DropdownNavbarItemDesktop from '@theme/NavbarItem/DropdownNavbarItem/Desktop'
|
||||
import DropdownNavbarItemMobile from '@theme/NavbarItem/DropdownNavbarItem/Mobile'
|
||||
import React from 'react'
|
||||
|
||||
export default function DropdownNavbarItem({ mobile = false, ...props }) {
|
||||
const Comp = mobile ? DropdownNavbarItemMobile : DropdownNavbarItemDesktop
|
||||
return <Comp {...props} />
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { Props } from '@theme/NavbarItem/DropdownNavbarItem'
|
||||
import type { ReactNode } from 'react'
|
||||
import DropdownNavbarItemDesktop from '@theme/NavbarItem/DropdownNavbarItem/Desktop'
|
||||
import DropdownNavbarItemMobile from '@theme/NavbarItem/DropdownNavbarItem/Mobile'
|
||||
import React from 'react'
|
||||
|
||||
export default function DropdownNavbarItem({
|
||||
mobile = false,
|
||||
...props
|
||||
}: Props): ReactNode {
|
||||
const Comp = mobile ? DropdownNavbarItemMobile : DropdownNavbarItemDesktop
|
||||
return <Comp {...props} />
|
||||
}
|
||||
Reference in new issue
Block a user