mirror of
https://github.com/wavetermdev/docusaurus-og.git
synced 2026-08-05 13:46:35 -07:00
feat: implement brand-guidelines theme and remote-content plugin
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Change Log
|
||||
@@ -0,0 +1,3 @@
|
||||
# Docusaurus Local Search
|
||||
|
||||
TODO.
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@acid-info/docusaurus-remote-content",
|
||||
"version": "1.0.0-alpha.0",
|
||||
"description": "Docusaurus remote content",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/plugin.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/acid-info/logos-docusaurus-plugins.git",
|
||||
"directory": "packages/docusaurus-remote-content"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"watch": "tsc --build --watch",
|
||||
"prepublishOnly": "yarn build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2",
|
||||
"fast-glob": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"tmp": "^0.2.1",
|
||||
"unzipper": "^0.10.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/core": "2.4.1",
|
||||
"@docusaurus/module-type-aliases": "2.4.1",
|
||||
"@docusaurus/types": "2.4.1",
|
||||
"@docusaurus/utils": "2.4.1",
|
||||
"@docusaurus/utils-common": "2.4.1",
|
||||
"@docusaurus/utils-validation": "2.4.1",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/tmp": "^0.2.6",
|
||||
"@types/unzipper": "^0.10.9"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import logger from '@docusaurus/logger'
|
||||
import type { LoadContext, Plugin } from '@docusaurus/types'
|
||||
import axios from 'axios'
|
||||
import * as fg from 'fast-glob'
|
||||
import * as fs from 'fs'
|
||||
import * as fsp from 'fs/promises'
|
||||
import * as path from 'path'
|
||||
import * as tmp from 'tmp'
|
||||
import unzipper from 'unzipper'
|
||||
|
||||
const copyFile = async (src: string, dest: string) => {
|
||||
const dirname = path.dirname(dest)
|
||||
if (!fs.existsSync(dirname)) {
|
||||
await fsp.mkdir(dirname, { recursive: true })
|
||||
}
|
||||
|
||||
await fsp.copyFile(src, dest)
|
||||
}
|
||||
|
||||
const copyContent = async (
|
||||
src: string,
|
||||
dest: string,
|
||||
options?: {
|
||||
keep?: string[]
|
||||
exclude?: string[]
|
||||
},
|
||||
) => {
|
||||
const { keep = [], exclude = [] } = options || {}
|
||||
|
||||
const tmpDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
|
||||
if (!fs.existsSync(dest)) await fsp.mkdir(dest, { recursive: true })
|
||||
|
||||
{
|
||||
const filenames = fg.sync(['**/*'], {
|
||||
cwd: src,
|
||||
ignore: exclude,
|
||||
absolute: false,
|
||||
})
|
||||
|
||||
for (const filename of filenames) {
|
||||
const absPath = path.join(src, filename)
|
||||
const stat = await fsp.stat(absPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
await fsp.mkdir(path.join(tmpDir.name, filename), {
|
||||
recursive: true,
|
||||
})
|
||||
} else await copyFile(absPath, path.join(tmpDir.name, filename))
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const filenames = fg.sync(keep, {
|
||||
cwd: dest,
|
||||
})
|
||||
|
||||
for (const filename of filenames) {
|
||||
const absPath = path.join(dest, filename)
|
||||
const stat = await fsp.stat(absPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
await fsp.mkdir(path.join(tmpDir.name, filename), {
|
||||
recursive: true,
|
||||
})
|
||||
} else await copyFile(absPath, path.join(tmpDir.name, filename))
|
||||
}
|
||||
}
|
||||
|
||||
await fsp.rm(dest, { recursive: true, force: true })
|
||||
await fsp.cp(tmpDir.name, dest, { recursive: true })
|
||||
|
||||
tmpDir.removeCallback()
|
||||
}
|
||||
|
||||
type PluginOptions = {
|
||||
remote: {
|
||||
type: 'zip'
|
||||
url: string
|
||||
dir?: string
|
||||
}
|
||||
|
||||
// The directory in the remote repository containing the content to be copied
|
||||
contentDir: string
|
||||
|
||||
// The directory in the local site to copy the content to
|
||||
outDir: string
|
||||
|
||||
// Exclude files matching these glob patterns
|
||||
exclude?: string[]
|
||||
|
||||
// Keep local files matching these glob patterns
|
||||
keep?: string[]
|
||||
|
||||
// Keep local static files matching these glob patterns
|
||||
keepStatic?: string[]
|
||||
}
|
||||
|
||||
export default async function remoteContentPlugin(
|
||||
context: LoadContext,
|
||||
options: PluginOptions,
|
||||
): Promise<Plugin<undefined>> {
|
||||
const tempDir = tmp.dirSync({ unsafeCleanup: true })
|
||||
const repoDir = path.join(tempDir.name, 'repo')
|
||||
const zipDir = path.join(tempDir.name, 'zip')
|
||||
|
||||
const downloadRemoteContent = async () => {
|
||||
const { remote, contentDir, outDir, exclude = [], keep = [] } = options
|
||||
|
||||
if (remote.type === 'zip') {
|
||||
const zip = await axios
|
||||
.get(remote.url, { responseType: 'stream' })
|
||||
.then((res) => res.data)
|
||||
const dest = unzipper.Extract({ path: zipDir })
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
zip.pipe(dest)
|
||||
|
||||
dest.on('close', resolve)
|
||||
dest.on('error', reject)
|
||||
})
|
||||
|
||||
if (remote.dir) {
|
||||
await fsp.rename(path.join(zipDir, remote.dir), repoDir)
|
||||
} else await fsp.rename(zipDir, repoDir)
|
||||
}
|
||||
}
|
||||
|
||||
const copyRemoteContent = async () => {
|
||||
const { remote, contentDir, outDir, exclude = [], keep = [] } = options
|
||||
|
||||
await copyContent(
|
||||
path.join(repoDir, contentDir),
|
||||
path.join(context.siteDir, outDir),
|
||||
{
|
||||
exclude,
|
||||
keep,
|
||||
},
|
||||
)
|
||||
|
||||
await copyContent(
|
||||
path.join(repoDir, 'static'),
|
||||
path.join(context.siteDir, 'static'),
|
||||
{
|
||||
exclude: [],
|
||||
keep: options.keepStatic ?? [],
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info`Downloading remote content from ${options.remote.url}`
|
||||
await downloadRemoteContent()
|
||||
await copyRemoteContent()
|
||||
tempDir.removeCallback()
|
||||
} catch (error) {
|
||||
tempDir.removeCallback()
|
||||
console.error(error)
|
||||
logger.error`Failed to download remote content from ${options.remote.url}`
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'docusaurus-remote-content',
|
||||
}
|
||||
}
|
||||
|
||||
export { type PluginOptions }
|
||||
@@ -0,0 +1 @@
|
||||
export type * from './index'
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "./lib/.tsbuildinfo",
|
||||
"rootDir": "src",
|
||||
"outDir": "lib",
|
||||
"lib": ["DOM"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/theme", "**/__tests__/**"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# Logos Docusaurus Brand Guidelines Theme
|
||||
@@ -0,0 +1,88 @@
|
||||
const _ = require('lodash')
|
||||
|
||||
const gulp = require('gulp')
|
||||
const path = require('path')
|
||||
const merge = require('merge2')
|
||||
const rimraf = require('rimraf')
|
||||
const ts = require('gulp-typescript')
|
||||
const TscWatch = require('tsc-watch/client')
|
||||
const sourcemaps = require('gulp-sourcemaps')
|
||||
const syncDirectory = require('sync-directory')
|
||||
const { replaceTscAliasPaths } = require('tsc-alias')
|
||||
|
||||
const project = ts.createProject('./tsconfig.client.json', {
|
||||
declaration: true,
|
||||
isolatedModules: false,
|
||||
})
|
||||
|
||||
const SOURCE_DIR = project.config.compilerOptions.rootDir ?? 'src'
|
||||
const OUT_DIR = project.config.compilerOptions.outDir ?? 'lib'
|
||||
|
||||
const sourceDir = path.resolve('./', SOURCE_DIR)
|
||||
const outDir = path.resolve('./', OUT_DIR)
|
||||
|
||||
const clean = async (cb) => {
|
||||
rimraf(outDir, cb)
|
||||
}
|
||||
|
||||
const build = (cb) => {
|
||||
return gulp.series(buildClient, copyFiles, postBuild, buildServer)(cb)
|
||||
}
|
||||
|
||||
const buildClient = () => {
|
||||
const compiled = project.src().pipe(sourcemaps.init()).pipe(project())
|
||||
|
||||
return merge(compiled.dts, compiled.js.pipe(sourcemaps.write())).pipe(
|
||||
gulp.dest(outDir),
|
||||
)
|
||||
}
|
||||
|
||||
const replaceTsAliasPaths = () =>
|
||||
replaceTscAliasPaths({
|
||||
configFile: './tsconfig.client.json',
|
||||
})
|
||||
|
||||
const postBuild = (done) =>
|
||||
gulp.series((cb) => replaceTsAliasPaths().finally(cb))(done)
|
||||
|
||||
const buildServer = () => {
|
||||
const project = ts.createProject('./tsconfig.json', {
|
||||
isolatedModules: false,
|
||||
})
|
||||
|
||||
const compiled = project.src().pipe(project())
|
||||
|
||||
return merge(compiled.dts, compiled.js).pipe(gulp.dest(outDir))
|
||||
}
|
||||
|
||||
const syncDirectories = async (watch = false) => {
|
||||
syncDirectory.async(sourceDir, outDir, {
|
||||
watch,
|
||||
verbose: 1,
|
||||
exclude: [/.*\.(ts|tsx)$/],
|
||||
})
|
||||
}
|
||||
|
||||
const watch = async () => {
|
||||
syncDirectories(true)
|
||||
|
||||
const watch = new TscWatch()
|
||||
|
||||
watch.on('success', async () => {
|
||||
await postBuild()
|
||||
})
|
||||
|
||||
watch.start('--build')
|
||||
}
|
||||
|
||||
const copyFiles = (cb) => {
|
||||
syncDirectories(false).finally(cb)
|
||||
}
|
||||
|
||||
gulp.task('build', build)
|
||||
gulp.task('watch', watch)
|
||||
gulp.task('clean', clean)
|
||||
gulp.task('build-server', buildServer)
|
||||
gulp.task('build-client', buildClient)
|
||||
gulp.task('post-build', postBuild)
|
||||
gulp.task('copy-files', copyFiles)
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@acid-info/logos-docusaurus-brand-guidelines-theme",
|
||||
"version": "1.0.0-alpha.0",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"types": "src/theme.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/acid-info/logos-docusaurus-plugins.git",
|
||||
"directory": "packages/logos-docusaurus-brand-guidelines-theme"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "yarn gulp clean",
|
||||
"prebuild": "yarn clean",
|
||||
"build": "yarn gulp build",
|
||||
"watch": "yarn gulp watch",
|
||||
"build:client": "yarn gulp build-client",
|
||||
"build:server": "yarn gulp build-server",
|
||||
"prepublishOnly": "yarn clean && yarn build"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@acid-info/logos-docusaurus-theme": "1.0.0-alpha.121",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/mdx-js__react": "^1.5.5",
|
||||
"@types/three": "^0.152.1",
|
||||
"glob": "^10.3.10",
|
||||
"react-docgen": "^7.0.0",
|
||||
"react-docgen-markdown-renderer": "^2.1.3",
|
||||
"sass": "^1.55.0",
|
||||
"tsc-alias": "^1.7.0",
|
||||
"tsc-watch": "^5.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.4 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0",
|
||||
"@acid-info/logos-docusaurus-theme": "1.0.0-alpha.121"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
.root {
|
||||
width: 100px;
|
||||
border: 1px solid rgb(var(--lsd-border-primary));
|
||||
}
|
||||
|
||||
.root.fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.root .color {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
||||
}
|
||||
|
||||
.root .info {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.root .title {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.root .variables {
|
||||
display: grid;
|
||||
gap: 8px 16px;
|
||||
grid-template: auto / auto 1fr;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import React from 'react'
|
||||
import styles from './ColorCard.module.scss'
|
||||
import clsx from 'clsx'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
|
||||
export type ColorCardProps = Omit<
|
||||
React.HTMLProps<HTMLDivElement>,
|
||||
'title' | 'color'
|
||||
> & {
|
||||
color: string
|
||||
title: React.ReactNode
|
||||
fullWidth?: boolean
|
||||
variables?: {
|
||||
name: string
|
||||
value: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export const ColorCard: React.FC<ColorCardProps> = ({
|
||||
title,
|
||||
color,
|
||||
fullWidth,
|
||||
variables = [],
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={clsx(
|
||||
props.className,
|
||||
styles.root,
|
||||
fullWidth && styles.fullWidth,
|
||||
)}
|
||||
>
|
||||
<div className={styles.color} style={{ background: color }}></div>
|
||||
<div className={styles.info}>
|
||||
<Typography
|
||||
className={styles.title}
|
||||
variant="subtitle1"
|
||||
component="div"
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<div className={styles.variables}>
|
||||
{variables.map((variable, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<Typography variant="label2">{variable.name}</Typography>
|
||||
<Typography variant="label2">{variable.value}</Typography>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './ColorCard'
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
.root {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid rgb(var(--lsd-border-primary));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
text-decoration: none !important;
|
||||
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
.root .title {
|
||||
padding: 16px;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.root .image {
|
||||
}
|
||||
|
||||
.root .imageContainer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import ThemedImage from '@theme/ThemedImage'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import styles from './ComponentCard.module.scss'
|
||||
import Link, { Props as LinkProps } from '@docusaurus/Link'
|
||||
|
||||
export type ComponentCardProps = Omit<LinkProps, 'title' | 'color'> & {
|
||||
title: React.ReactNode
|
||||
imageSrc?: string
|
||||
imageDarkSrc?: string
|
||||
imageWidth?: string | number
|
||||
imageHeight?: string | number
|
||||
}
|
||||
|
||||
export const ComponentCard: React.FC<ComponentCardProps> = ({
|
||||
title,
|
||||
imageSrc,
|
||||
imageDarkSrc,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<Link {...props} className={clsx(props.className, styles.root)}>
|
||||
<Typography className={styles.title} component="div">
|
||||
{title}
|
||||
</Typography>
|
||||
<div className={styles.imageContainer}>
|
||||
<ThemedImage
|
||||
width={imageWidth}
|
||||
height={imageHeight}
|
||||
sources={{
|
||||
dark: imageDarkSrc || imageSrc || '',
|
||||
light: imageSrc || imageDarkSrc || '',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './ComponentCard'
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
@use '@acid-info/logos-docusaurus-theme/lib/client/css/utils';
|
||||
|
||||
.root {
|
||||
}
|
||||
|
||||
@include utils.responsive('md', 'up') {
|
||||
.root .item {
|
||||
&:nth-child(even) > a {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
&:not(.lastRow) > a {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('md', 'down') {
|
||||
.root .item {
|
||||
&:not(:last-child) > a {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Grid,
|
||||
GridProps,
|
||||
} from '@acid-info/logos-docusaurus-theme/lib/client/components/mdx'
|
||||
import React from 'react'
|
||||
import { ComponentCard, ComponentCardProps } from '../ComponentCard'
|
||||
import styles from './ComponentGrid.module.scss'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export type ComponentGridProps = GridProps & {
|
||||
list: ComponentCardProps[]
|
||||
}
|
||||
|
||||
export const ComponentGrid: React.FC<ComponentGridProps> = ({
|
||||
list = [],
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<Grid
|
||||
md={{ cols: 2, wrap: true, gap: '0' }}
|
||||
{...props}
|
||||
className={clsx(styles.root, props.className)}
|
||||
>
|
||||
{list.map((props, idx) => (
|
||||
<Grid.Item
|
||||
md={1}
|
||||
key={idx}
|
||||
className={clsx(
|
||||
styles.item,
|
||||
idx >= list.length - 2 && styles.lastRow,
|
||||
)}
|
||||
>
|
||||
<ComponentCard {...props} />
|
||||
</Grid.Item>
|
||||
))}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './ComponentGrid'
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
@use '@acid-info/logos-docusaurus-theme/lib/client/css/utils';
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background-color: rgb(var(--lsd-theme-secondary));
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.enlarged {
|
||||
position: fixed;
|
||||
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
max-height: 90vh;
|
||||
max-width: 90vw;
|
||||
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
position: fixed;
|
||||
top: 18px;
|
||||
right: 16px;
|
||||
|
||||
background-color: rgb(var(--lsd-theme-secondary)) !important;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
z-index: 1001;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user