mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-06-06 12:26:38 +00:00
📝 Docs: 商店插件可用性筛选 & 更新排序 (#3334)
This commit is contained in:
parent
db857b11fa
commit
6cff660af0
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "root",
|
"name": "root",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"packageManager": "yarn@1.22.22",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"website"
|
"website"
|
||||||
],
|
],
|
||||||
|
@ -3,14 +3,6 @@ import type { Options as ChangelogOptions } from "@nullbot/docusaurus-plugin-cha
|
|||||||
import type * as Preset from "@nullbot/docusaurus-preset-nonepress";
|
import type * as Preset from "@nullbot/docusaurus-preset-nonepress";
|
||||||
import { themes } from "prism-react-renderer";
|
import { themes } from "prism-react-renderer";
|
||||||
|
|
||||||
// By default, we use Docusaurus Faster
|
|
||||||
// DOCUSAURUS_SLOWER=true is useful for benchmarking faster against slower
|
|
||||||
// hyperfine --prepare 'yarn clear:website' --runs 3 'DOCUSAURUS_SLOWER=true yarn build:website:fast' 'yarn build:website:fast'
|
|
||||||
const isSlower = process.env.DOCUSAURUS_SLOWER === "true";
|
|
||||||
if (isSlower) {
|
|
||||||
console.log("🐢 Using slower Docusaurus build");
|
|
||||||
}
|
|
||||||
|
|
||||||
// color mode config
|
// color mode config
|
||||||
const colorMode: Preset.ThemeConfig["colorMode"] = {
|
const colorMode: Preset.ThemeConfig["colorMode"] = {
|
||||||
defaultMode: "light",
|
defaultMode: "light",
|
||||||
|
@ -13,37 +13,38 @@
|
|||||||
"docusaurus": "docusaurus",
|
"docusaurus": "docusaurus",
|
||||||
"start": "docusaurus start --host 0.0.0.0 --port 3000",
|
"start": "docusaurus start --host 0.0.0.0 --port 3000",
|
||||||
"build": "docusaurus build",
|
"build": "docusaurus build",
|
||||||
|
"build:fast": "cross-env BUILD_FAST=true yarn build",
|
||||||
|
"build:fast:rsdoctor": "cross-env BUILD_FAST=true RSDOCTOR=true yarn build",
|
||||||
|
"build:fast:profile": "cross-env BUILD_FAST=true node --cpu-prof --cpu-prof-dir .cpu-prof ./node_modules/.bin/docusaurus build",
|
||||||
"swizzle": "docusaurus swizzle",
|
"swizzle": "docusaurus swizzle",
|
||||||
"deploy": "docusaurus deploy",
|
"deploy": "docusaurus deploy",
|
||||||
"clear": "docusaurus clear",
|
"clear": "docusaurus clear",
|
||||||
"serve": "docusaurus serve",
|
"serve": "docusaurus serve",
|
||||||
"write-translations": "docusaurus write-translations",
|
"write-translations": "docusaurus write-translations",
|
||||||
"write-heading-ids": "docusaurus write-heading-ids",
|
"write-heading-ids": "docusaurus write-heading-ids",
|
||||||
"typecheck": "tsc"
|
"typecheck": "tsc",
|
||||||
|
"prettier": "prettier --config ../.prettierrc --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "^3.6.2",
|
"@docusaurus/core": "^3.7.0",
|
||||||
"@mdx-js/react": "^3.0.0",
|
"@mdx-js/react": "^3.0.0",
|
||||||
"@nullbot/docusaurus-plugin-changelog": "^3.0.0",
|
"@nullbot/docusaurus-plugin-changelog": "^3.0.0",
|
||||||
"@nullbot/docusaurus-preset-nonepress": "^3.0.0",
|
"@nullbot/docusaurus-preset-nonepress": "^3.0.0",
|
||||||
"@swc/core": "^1.7.26",
|
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"copy-text-to-clipboard": "^3.2.0",
|
"copy-text-to-clipboard": "^3.2.0",
|
||||||
"prism-react-renderer": "^2.3.0",
|
"prism-react-renderer": "^2.3.0",
|
||||||
"raw-loader": "^4.0.2",
|
"react": "^19.0.0",
|
||||||
"react": "^18.0.0",
|
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-use-pagination": "^2.0.1",
|
"react-use-pagination": "^2.0.1"
|
||||||
"swc-loader": "^0.2.6"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/faster": "^3.6.2",
|
"@docusaurus/faster": "^3.7.0",
|
||||||
"@docusaurus/module-type-aliases": "^3.6.2",
|
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||||
"@nullbot/docusaurus-tsconfig": "^3.0.0",
|
"@nullbot/docusaurus-tsconfig": "^3.0.0",
|
||||||
"@types/react-color": "^3.0.10",
|
"@types/react-color": "^3.0.10",
|
||||||
"asciinema-player": "^3.5.0",
|
"asciinema-player": "^3.5.0",
|
||||||
"typescript": "~5.5.2"
|
"typescript": "~5.7.2"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
@ -25,7 +25,7 @@ export type Props = {
|
|||||||
export default function AsciinemaContainer({
|
export default function AsciinemaContainer({
|
||||||
url,
|
url,
|
||||||
options = {},
|
options = {},
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import "asciinema-player/dist/bundle/asciinema-player.css";
|
|
||||||
import BrowserOnly from "@docusaurus/BrowserOnly";
|
import BrowserOnly from "@docusaurus/BrowserOnly";
|
||||||
|
import "asciinema-player/dist/bundle/asciinema-player.css";
|
||||||
|
|
||||||
import "./styles.css";
|
|
||||||
import type { Props } from "./container";
|
import type { Props } from "./container";
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
export type { Props } from "./container";
|
export type { Props } from "./container";
|
||||||
|
|
||||||
export default function Asciinema(props: Props): JSX.Element {
|
export default function Asciinema(props: Props): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<BrowserOnly
|
<BrowserOnly
|
||||||
fallback={
|
fallback={
|
||||||
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
|
|
||||||
import { Form } from ".";
|
import { Form } from ".";
|
||||||
|
|
||||||
export default function AdapterForm(): JSX.Element {
|
export default function AdapterForm(): React.ReactNode {
|
||||||
const formItems = [
|
const formItems = [
|
||||||
{
|
{
|
||||||
name: "基本信息",
|
name: "基本信息",
|
||||||
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
|
|
||||||
import { Form } from ".";
|
import { Form } from ".";
|
||||||
|
|
||||||
export default function BotForm(): JSX.Element {
|
export default function BotForm(): React.ReactNode {
|
||||||
const formItems = [
|
const formItems = [
|
||||||
{
|
{
|
||||||
name: "基本信息",
|
name: "基本信息",
|
||||||
|
@ -18,7 +18,7 @@ export type Props = {
|
|||||||
export default function TagFormItem({
|
export default function TagFormItem({
|
||||||
allowTags,
|
allowTags,
|
||||||
onTagUpdate,
|
onTagUpdate,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const [tags, setTags] = useState<TagType[]>([]);
|
const [tags, setTags] = useState<TagType[]>([]);
|
||||||
const [label, setLabel] = useState<TagType["label"]>("");
|
const [label, setLabel] = useState<TagType["label"]>("");
|
||||||
const [color, setColor] = useState<TagType["color"]>("#ea5252");
|
const [color, setColor] = useState<TagType["color"]>("#ea5252");
|
||||||
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
|
|
||||||
import { Form } from ".";
|
import { Form } from ".";
|
||||||
|
|
||||||
export default function PluginForm(): JSX.Element {
|
export default function PluginForm(): React.ReactNode {
|
||||||
const formItems = [
|
const formItems = [
|
||||||
{
|
{
|
||||||
name: "包信息",
|
name: "包信息",
|
||||||
|
@ -32,7 +32,7 @@ export function Form({
|
|||||||
children,
|
children,
|
||||||
formItems,
|
formItems,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const [currentStep, setCurrentStep] = useState<number>(0);
|
const [currentStep, setCurrentStep] = useState<number>(0);
|
||||||
const [result, setResult] = useState<Record<string, string>>({});
|
const [result, setResult] = useState<Record<string, string>>({});
|
||||||
const [allowTags, setAllowTags] = useState<TagType[]>([]);
|
const [allowTags, setAllowTags] = useState<TagType[]>([]);
|
||||||
@ -125,7 +125,7 @@ export function FormItem({
|
|||||||
allowTags: TagType[];
|
allowTags: TagType[];
|
||||||
result: Record<string, string>;
|
result: Record<string, string>;
|
||||||
setResult: (key: string, value: string) => void;
|
setResult: (key: string, value: string) => void;
|
||||||
}): JSX.Element {
|
}): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<label className="label">
|
<label className="label">
|
||||||
|
@ -16,7 +16,7 @@ export function HomeFeature({
|
|||||||
description,
|
description,
|
||||||
annotaion,
|
annotaion,
|
||||||
children,
|
children,
|
||||||
}: Feature): JSX.Element {
|
}: Feature): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center p-4">
|
<div className="flex flex-col items-center justify-center p-4">
|
||||||
<p className="text-sm text-base-content/70 font-medium tracking-wide uppercase">
|
<p className="text-sm text-base-content/70 font-medium tracking-wide uppercase">
|
||||||
@ -32,7 +32,7 @@ export function HomeFeature({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeFeatureSingleColumn(props: Feature): JSX.Element {
|
function HomeFeatureSingleColumn(props: Feature): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 px-4 py-8 md:px-16 mx-auto">
|
<div className="grid grid-cols-1 px-4 py-8 md:px-16 mx-auto">
|
||||||
<HomeFeature {...props} />
|
<HomeFeature {...props} />
|
||||||
@ -46,7 +46,7 @@ function HomeFeatureDoubleColumn({
|
|||||||
}: {
|
}: {
|
||||||
features: [Feature, Feature];
|
features: [Feature, Feature];
|
||||||
children?: [React.ReactNode, React.ReactNode];
|
children?: [React.ReactNode, React.ReactNode];
|
||||||
}): JSX.Element {
|
}): React.ReactNode {
|
||||||
const [children1, children2] = children ?? [];
|
const [children1, children2] = children ?? [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -57,7 +57,7 @@ function HomeFeatureDoubleColumn({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeFeatures(): JSX.Element {
|
function HomeFeatures(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HomeFeatureSingleColumn
|
<HomeFeatureSingleColumn
|
||||||
|
@ -10,7 +10,7 @@ import copy from "copy-text-to-clipboard";
|
|||||||
import IconCopy from "@theme/Icon/Copy";
|
import IconCopy from "@theme/Icon/Copy";
|
||||||
import IconSuccess from "@theme/Icon/Success";
|
import IconSuccess from "@theme/Icon/Success";
|
||||||
|
|
||||||
function HomeHeroInstallButton(): JSX.Element {
|
function HomeHeroInstallButton(): React.ReactNode {
|
||||||
const code = "pipx run nb-cli create";
|
const code = "pipx run nb-cli create";
|
||||||
|
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
@ -37,7 +37,7 @@ function HomeHeroInstallButton(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomeHero(): JSX.Element {
|
function HomeHero(): React.ReactNode {
|
||||||
const {
|
const {
|
||||||
siteConfig: { tagline },
|
siteConfig: { tagline },
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import "./styles.css";
|
|
||||||
import HomeFeatures from "./Feature";
|
import HomeFeatures from "./Feature";
|
||||||
import HomeHero from "./Hero";
|
import HomeHero from "./Hero";
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
export default function HomeContent(): JSX.Element {
|
export default function HomeContent(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="home-container">
|
<div className="home-container">
|
||||||
<HomeHero />
|
<HomeHero />
|
||||||
|
@ -6,8 +6,8 @@ import useBaseUrl from "@docusaurus/useBaseUrl";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useNonepressThemeConfig } from "@nullbot/docusaurus-theme-nonepress/client";
|
import { useNonepressThemeConfig } from "@nullbot/docusaurus-theme-nonepress/client";
|
||||||
|
|
||||||
import "./styles.css";
|
|
||||||
import ThemedImage from "@theme/ThemedImage";
|
import ThemedImage from "@theme/ThemedImage";
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
export type Message = {
|
export type Message = {
|
||||||
msg: string;
|
msg: string;
|
||||||
@ -19,7 +19,7 @@ function MessageBox({
|
|||||||
msg,
|
msg,
|
||||||
position = "left",
|
position = "left",
|
||||||
monospace = false,
|
monospace = false,
|
||||||
}: Message): JSX.Element {
|
}: Message): React.ReactNode {
|
||||||
const {
|
const {
|
||||||
navbar: { logo },
|
navbar: { logo },
|
||||||
} = useNonepressThemeConfig();
|
} = useNonepressThemeConfig();
|
||||||
@ -63,7 +63,7 @@ export default function Messenger({
|
|||||||
msgs = [],
|
msgs = [],
|
||||||
}: {
|
}: {
|
||||||
msgs?: Message[];
|
msgs?: Message[];
|
||||||
}): JSX.Element {
|
}): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="messenger-container">
|
<div className="messenger-container">
|
||||||
<header className="messenger-title">
|
<header className="messenger-title">
|
||||||
|
@ -22,7 +22,7 @@ export default function Modal({
|
|||||||
useCustomTitle,
|
useCustomTitle,
|
||||||
backdropExit,
|
backdropExit,
|
||||||
title,
|
title,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const [transitionClass, setTransitionClass] = useState<string>("");
|
const [transitionClass, setTransitionClass] = useState<string>("");
|
||||||
|
|
||||||
const onFadeIn = () => setTransitionClass("fade-in");
|
const onFadeIn = () => setTransitionClass("fade-in");
|
||||||
|
@ -31,7 +31,7 @@ export default function Paginate({
|
|||||||
setPage,
|
setPage,
|
||||||
previousEnabled,
|
previousEnabled,
|
||||||
nextEnabled,
|
nextEnabled,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
// const [containerElement, setContainerElement] = useState<HTMLElement | null>(
|
// const [containerElement, setContainerElement] = useState<HTMLElement | null>(
|
||||||
// null
|
// null
|
||||||
// );
|
// );
|
||||||
|
@ -5,10 +5,10 @@ import clsx from "clsx";
|
|||||||
import Link from "@docusaurus/Link";
|
import Link from "@docusaurus/Link";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import "./styles.css";
|
|
||||||
import Tag from "@/components/Resource/Tag";
|
import Tag from "@/components/Resource/Tag";
|
||||||
import ValidStatus from "@/components/Resource/ValidStatus";
|
import ValidStatus from "@/components/Resource/ValidStatus";
|
||||||
import type { Resource } from "@/libs/store";
|
import type { Resource } from "@/libs/store";
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
resource: Resource;
|
resource: Resource;
|
||||||
@ -24,7 +24,7 @@ export default function ResourceCard({
|
|||||||
onTagClick,
|
onTagClick,
|
||||||
onAuthorClick,
|
onAuthorClick,
|
||||||
className,
|
className,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const isGithub = /^https:\/\/github.com\/[^/]+\/[^/]+/.test(
|
const isGithub = /^https:\/\/github.com\/[^/]+\/[^/]+/.test(
|
||||||
resource.homepage
|
resource.homepage
|
||||||
);
|
);
|
||||||
|
@ -72,6 +72,15 @@ export default function ResourceDetailCard({ resource }: Props) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPluginStatusUpdatedTime = (resource: Resource) => {
|
||||||
|
switch (resource.resourceType) {
|
||||||
|
case "plugin":
|
||||||
|
return new Date(resource.time).toLocaleString();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchPypiProject = (projectName: string) =>
|
const fetchPypiProject = (projectName: string) =>
|
||||||
fetch(`https://pypi.org/pypi/${projectName}/json`)
|
fetch(`https://pypi.org/pypi/${projectName}/json`)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
@ -99,8 +108,9 @@ export default function ResourceDetailCard({ resource }: Props) {
|
|||||||
|
|
||||||
const projectLink = getProjectLink(resource) || "无";
|
const projectLink = getProjectLink(resource) || "无";
|
||||||
const moduleName = getModuleName(resource) || "无";
|
const moduleName = getModuleName(resource) || "无";
|
||||||
const homepageLink = getHomepageLink(resource) || undefined;
|
const homepageLink = getHomepageLink(resource);
|
||||||
const pypiProjectLink = getPypiProjectLink(resource) || undefined;
|
const pypiProjectLink = getPypiProjectLink(resource);
|
||||||
|
const updatedTime = getPluginStatusUpdatedTime(resource);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -183,31 +193,39 @@ export default function ResourceDetailCard({ resource }: Props) {
|
|||||||
{(pypiData && pypiData.info.version) || "无"}
|
{(pypiData && pypiData.info.version) || "无"}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="detail-card-meta-item">
|
{homepageLink && (
|
||||||
<FontAwesomeIcon fixedWidth icon={["fas", "fingerprint"]} />{" "}
|
<div className="detail-card-meta-item">
|
||||||
<a
|
<FontAwesomeIcon fixedWidth icon={["fas", "fingerprint"]} />{" "}
|
||||||
href={homepageLink}
|
<a
|
||||||
target="_blank"
|
href={homepageLink}
|
||||||
rel="noreferrer"
|
target="_blank"
|
||||||
className={homepageLink && "hover:underline hover:text-primary"}
|
rel="noreferrer"
|
||||||
>
|
className="detail-card-meta-item-link"
|
||||||
{moduleName}
|
>
|
||||||
</a>
|
{moduleName}
|
||||||
</div>
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{pypiProjectLink && (
|
||||||
|
<div className="detail-card-meta-item">
|
||||||
|
<FontAwesomeIcon fixedWidth icon={["fas", "cubes"]} />{" "}
|
||||||
|
<a
|
||||||
|
href={pypiProjectLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="detail-card-meta-item-link"
|
||||||
|
>
|
||||||
|
{projectLink}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="detail-card-meta-item">
|
<div className="detail-card-meta-item">
|
||||||
<FontAwesomeIcon fixedWidth icon={["fas", "cubes"]} />{" "}
|
<FontAwesomeIcon fixedWidth icon={["fas", "clock-rotate-left"]} />{" "}
|
||||||
<a
|
{updatedTime}
|
||||||
href={pypiProjectLink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={
|
|
||||||
pypiProjectLink && "hover:underline hover:text-primary"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{projectLink}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="detail-card-actions">
|
<div className="detail-card-actions">
|
||||||
<ValidStatus
|
<ValidStatus
|
||||||
resource={resource}
|
resource={resource}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-actions {
|
&-actions {
|
||||||
@apply flex items-center gap-x-2 ml-auto;
|
@apply flex items-center gap-x-2 lg:ml-auto;
|
||||||
|
|
||||||
&-button {
|
&-button {
|
||||||
@apply btn btn-sm;
|
@apply btn btn-sm;
|
||||||
@ -51,11 +51,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-right {
|
&-right {
|
||||||
@apply flex flex-col justify-start gap-y-2 lg:basis-1/4 max-w-[45%];
|
@apply flex flex-col justify-start gap-y-2 lg:basis-1/4 lg:max-w-[45%];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-meta-item {
|
&-meta-item {
|
||||||
@apply text-sm truncate;
|
@apply text-sm truncate;
|
||||||
|
|
||||||
|
&-link {
|
||||||
|
@apply hover:text-primary hover:transition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ import React from "react";
|
|||||||
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
import "./styles.css";
|
|
||||||
import { pickTextColor } from "@/libs/color";
|
import { pickTextColor } from "@/libs/color";
|
||||||
import type { Tag } from "@/types/tag";
|
import type { Tag } from "@/types/tag";
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
export type Props = Tag & {
|
export type Props = Tag & {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -16,7 +16,7 @@ export default function ResourceTag({
|
|||||||
color,
|
color,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={clsx("resource-tag", className)}
|
className={clsx("resource-tag", className)}
|
||||||
|
@ -2,9 +2,9 @@ import React, { useRef } from "react";
|
|||||||
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
import "./styles.css";
|
|
||||||
import { translate } from "@docusaurus/Translate";
|
import { translate } from "@docusaurus/Translate";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
@ -28,7 +28,7 @@ export default function Searcher({
|
|||||||
className,
|
className,
|
||||||
placeholder,
|
placeholder,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const ref = useRef<HTMLInputElement>(null);
|
const ref = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent<HTMLInputElement>) => {
|
const handleSubmit = (e: React.FormEvent<HTMLInputElement>) => {
|
||||||
@ -85,6 +85,10 @@ export default function Searcher({
|
|||||||
onClick={() => onTagClick(index)}
|
onClick={() => onTagClick(index)}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
|
<FontAwesomeIcon
|
||||||
|
className="searcher-action-icon close ml-1"
|
||||||
|
icon={["fas", "xmark"]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<input
|
<input
|
||||||
|
@ -18,7 +18,7 @@ import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
|||||||
import { useToolbar } from "@/libs/toolbar";
|
import { useToolbar } from "@/libs/toolbar";
|
||||||
import type { Adapter } from "@/types/adapter";
|
import type { Adapter } from "@/types/adapter";
|
||||||
|
|
||||||
export default function AdapterPage(): JSX.Element {
|
export default function AdapterPage(): React.ReactNode {
|
||||||
const [adapters, setAdapters] = useState<Adapter[] | null>(null);
|
const [adapters, setAdapters] = useState<Adapter[] | null>(null);
|
||||||
const adapterCount = adapters?.length ?? 0;
|
const adapterCount = adapters?.length ?? 0;
|
||||||
const loading = adapters === null;
|
const loading = adapters === null;
|
||||||
|
@ -17,7 +17,7 @@ import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
|||||||
import { useToolbar } from "@/libs/toolbar";
|
import { useToolbar } from "@/libs/toolbar";
|
||||||
import type { Bot } from "@/types/bot";
|
import type { Bot } from "@/types/bot";
|
||||||
|
|
||||||
export default function PluginPage(): JSX.Element {
|
export default function PluginPage(): React.ReactNode {
|
||||||
const [bots, setBots] = useState<Bot[] | null>(null);
|
const [bots, setBots] = useState<Bot[] | null>(null);
|
||||||
const botCount = bots?.length ?? 0;
|
const botCount = bots?.length ?? 0;
|
||||||
const loading = bots === null;
|
const loading = bots === null;
|
||||||
|
@ -15,7 +15,7 @@ import { useSearchControl } from "@/libs/search";
|
|||||||
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
||||||
import type { Driver } from "@/types/driver";
|
import type { Driver } from "@/types/driver";
|
||||||
|
|
||||||
export default function DriverPage(): JSX.Element {
|
export default function DriverPage(): React.ReactNode {
|
||||||
const [drivers, setDrivers] = useState<Driver[] | null>(null);
|
const [drivers, setDrivers] = useState<Driver[] | null>(null);
|
||||||
const driverCount = drivers?.length ?? 0;
|
const driverCount = drivers?.length ?? 0;
|
||||||
const loading = drivers === null;
|
const loading = drivers === null;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import Translate from "@docusaurus/Translate";
|
import Translate, { translate } from "@docusaurus/Translate";
|
||||||
import { usePagination } from "react-use-pagination";
|
import { usePagination } from "react-use-pagination";
|
||||||
|
|
||||||
import Admonition from "@theme/Admonition";
|
import Admonition from "@theme/Admonition";
|
||||||
@ -11,14 +11,18 @@ import Paginate from "@/components/Paginate";
|
|||||||
import ResourceCard from "@/components/Resource/Card";
|
import ResourceCard from "@/components/Resource/Card";
|
||||||
import ResourceDetailCard from "@/components/Resource/DetailCard";
|
import ResourceDetailCard from "@/components/Resource/DetailCard";
|
||||||
import Searcher from "@/components/Searcher";
|
import Searcher from "@/components/Searcher";
|
||||||
import StoreToolbar, { type Action } from "@/components/Store/Toolbar";
|
import StoreToolbar, {
|
||||||
|
type Action,
|
||||||
|
type Sorter,
|
||||||
|
} from "@/components/Store/Toolbar";
|
||||||
import { authorFilter, tagFilter } from "@/libs/filter";
|
import { authorFilter, tagFilter } from "@/libs/filter";
|
||||||
import { useSearchControl } from "@/libs/search";
|
import { useSearchControl } from "@/libs/search";
|
||||||
|
import { SortMode } from "@/libs/sorter";
|
||||||
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
||||||
import { useToolbar } from "@/libs/toolbar";
|
import { useToolbar } from "@/libs/toolbar";
|
||||||
import type { Plugin } from "@/types/plugin";
|
import type { Plugin } from "@/types/plugin";
|
||||||
|
|
||||||
export default function PluginPage(): JSX.Element {
|
export default function PluginPage(): React.ReactNode {
|
||||||
const [plugins, setPlugins] = useState<Plugin[] | null>(null);
|
const [plugins, setPlugins] = useState<Plugin[] | null>(null);
|
||||||
const pluginCount = plugins?.length ?? 0;
|
const pluginCount = plugins?.length ?? 0;
|
||||||
const loading = plugins === null;
|
const loading = plugins === null;
|
||||||
@ -27,6 +31,38 @@ export default function PluginPage(): JSX.Element {
|
|||||||
const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
|
const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
|
||||||
const [isOpenCardModal, setIsOpenCardModal] = useState<boolean>(false);
|
const [isOpenCardModal, setIsOpenCardModal] = useState<boolean>(false);
|
||||||
const [clickedPlugin, setClickedPlugin] = useState<Plugin | null>(null);
|
const [clickedPlugin, setClickedPlugin] = useState<Plugin | null>(null);
|
||||||
|
const [sortMode, setSortMode] = useState<SortMode>(SortMode.Default);
|
||||||
|
|
||||||
|
const sorterTool: Sorter = {
|
||||||
|
label:
|
||||||
|
sortMode === SortMode.Default
|
||||||
|
? translate({
|
||||||
|
id: "pages.store.sorter.label.default",
|
||||||
|
description: "The label of default sorter",
|
||||||
|
message: "默认顺序",
|
||||||
|
})
|
||||||
|
: translate({
|
||||||
|
id: "pages.store.sorter.label.updateDesc",
|
||||||
|
description: "The label of updateDesc sorter",
|
||||||
|
message: "更新时间倒序",
|
||||||
|
}),
|
||||||
|
icon: ["fas", "sort-amount-down"],
|
||||||
|
active: sortMode === SortMode.UpdateDesc,
|
||||||
|
onClick: () => {
|
||||||
|
setSortMode(
|
||||||
|
sortMode === SortMode.Default ? SortMode.UpdateDesc : SortMode.Default
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSortedPlugins = (plugins: Plugin[]): Plugin[] => {
|
||||||
|
if (sortMode === SortMode.UpdateDesc) {
|
||||||
|
return [...plugins].sort(
|
||||||
|
(a, b) => new Date(b.time).getTime() - new Date(a.time).getTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return plugins;
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
filteredResources: filteredPlugins,
|
filteredResources: filteredPlugins,
|
||||||
@ -37,7 +73,7 @@ export default function PluginPage(): JSX.Element {
|
|||||||
onSearchBackspace,
|
onSearchBackspace,
|
||||||
onSearchClear,
|
onSearchClear,
|
||||||
onSearchTagClick,
|
onSearchTagClick,
|
||||||
} = useSearchControl<Plugin>(plugins ?? []);
|
} = useSearchControl<Plugin>(getSortedPlugins(plugins ?? []));
|
||||||
const filteredPluginCount = filteredPlugins.length;
|
const filteredPluginCount = filteredPlugins.length;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -134,6 +170,7 @@ export default function PluginPage(): JSX.Element {
|
|||||||
<StoreToolbar
|
<StoreToolbar
|
||||||
className="not-prose"
|
className="not-prose"
|
||||||
filters={filterTools}
|
filters={filterTools}
|
||||||
|
sorter={sorterTool}
|
||||||
action={actionTool}
|
action={actionTool}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ type Props = {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
function StorePage({ title, children }: Props): JSX.Element {
|
function StorePage({ title, children }: Props): React.ReactNode {
|
||||||
const sidebarItems = useVersionedSidebar(
|
const sidebarItems = useVersionedSidebar(
|
||||||
useDocsVersionCandidates()[0].name,
|
useDocsVersionCandidates()[0].name,
|
||||||
SIDEBAR_ID
|
SIDEBAR_ID
|
||||||
@ -35,7 +35,10 @@ function StorePage({ title, children }: Props): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StoreLayout({ title, ...props }: Props): JSX.Element {
|
export default function StoreLayout({
|
||||||
|
title,
|
||||||
|
...props
|
||||||
|
}: Props): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMetadata title={title} />
|
<PageMetadata title={title} />
|
||||||
|
@ -12,6 +12,13 @@ export type Filter = {
|
|||||||
onSubmit: (query: string) => void;
|
onSubmit: (query: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Sorter = {
|
||||||
|
label: string;
|
||||||
|
icon: IconProp;
|
||||||
|
active: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
export type Action = {
|
export type Action = {
|
||||||
label: string;
|
label: string;
|
||||||
icon: IconProp;
|
icon: IconProp;
|
||||||
@ -20,6 +27,7 @@ export type Action = {
|
|||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
filters?: Filter[];
|
filters?: Filter[];
|
||||||
|
sorter?: Sorter;
|
||||||
action?: Action;
|
action?: Action;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
@ -29,7 +37,7 @@ function ToolbarFilter({
|
|||||||
icon,
|
icon,
|
||||||
choices,
|
choices,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: Filter): JSX.Element {
|
}: Filter): React.ReactNode {
|
||||||
const [query, setQuery] = useState<string>("");
|
const [query, setQuery] = useState<string>("");
|
||||||
|
|
||||||
const filteredChoices = choices
|
const filteredChoices = choices
|
||||||
@ -96,33 +104,65 @@ function ToolbarFilter({
|
|||||||
|
|
||||||
export default function StoreToolbar({
|
export default function StoreToolbar({
|
||||||
filters,
|
filters,
|
||||||
|
sorter,
|
||||||
action,
|
action,
|
||||||
className,
|
className,
|
||||||
}: Props): JSX.Element | null {
|
}: Props): React.ReactNode | null {
|
||||||
if (!(filters && filters.length > 0) && !action) {
|
if (!(filters && filters.length > 0) && !action) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx("store-toolbar", className)}>
|
<>
|
||||||
{filters && filters.length > 0 && (
|
<div className={clsx("store-toolbar", className)}>
|
||||||
<div className="store-toolbar-filters">
|
<div className="store-toolbar-filters">
|
||||||
{filters.map((filter, index) => (
|
{filters?.map((filter, index) => (
|
||||||
<ToolbarFilter key={index} {...filter} />
|
<ToolbarFilter key={index} {...filter} />
|
||||||
))}
|
))}
|
||||||
|
{sorter && (
|
||||||
|
<div className="store-toolbar-sorter store-toolbar-sorter-desktop">
|
||||||
|
<button
|
||||||
|
className={clsx(
|
||||||
|
"btn btn-sm btn-primary no-animation mr-2",
|
||||||
|
!sorter.active && "btn-outline"
|
||||||
|
)}
|
||||||
|
onClick={sorter.onClick}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={sorter.icon} />
|
||||||
|
{sorter.label}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{action && (
|
{action && (
|
||||||
<div className="store-toolbar-action">
|
<div className="store-toolbar-action">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary no-animation"
|
className="btn btn-sm btn-primary no-animation"
|
||||||
onClick={action.onClick}
|
onClick={action.onClick}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={action.icon} />
|
<FontAwesomeIcon icon={action.icon} />
|
||||||
{action.label}
|
{action.label}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className={clsx("store-toolbar store-toolbar-second", className)}>
|
||||||
|
{sorter && (
|
||||||
|
<div className="store-toolbar-sorter">
|
||||||
|
<button
|
||||||
|
className={clsx(
|
||||||
|
"btn btn-sm btn-primary no-animation mr-2",
|
||||||
|
!sorter.active && "btn-outline"
|
||||||
|
)}
|
||||||
|
onClick={sorter.onClick}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={sorter.icon} />
|
||||||
|
{sorter.label}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,18 @@
|
|||||||
&-toolbar {
|
&-toolbar {
|
||||||
@apply flex items-center justify-center my-4;
|
@apply flex items-center justify-center my-4;
|
||||||
|
|
||||||
|
&-second {
|
||||||
|
@apply lg:hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-sorter {
|
||||||
|
@apply max-lg:flex-1;
|
||||||
|
|
||||||
|
&-desktop {
|
||||||
|
@apply max-lg:hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-filters {
|
&-filters {
|
||||||
@apply flex grow gap-2;
|
@apply flex grow gap-2;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ export default function Tag({
|
|||||||
}: TagType & {
|
}: TagType & {
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: React.MouseEventHandler<HTMLSpanElement>;
|
onClick?: React.MouseEventHandler<HTMLSpanElement>;
|
||||||
}): JSX.Element {
|
}): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={clsx("tag", className)}
|
className={clsx("tag", className)}
|
||||||
|
@ -3,14 +3,52 @@ import { useCallback, useState } from "react";
|
|||||||
import { translate } from "@docusaurus/Translate";
|
import { translate } from "@docusaurus/Translate";
|
||||||
|
|
||||||
import type { Resource } from "./store";
|
import type { Resource } from "./store";
|
||||||
|
import { ValidStatus } from "./valid";
|
||||||
|
|
||||||
|
import { getValidStatus } from "@/components/Resource/ValidStatus";
|
||||||
|
|
||||||
export type Filter<T extends Resource = Resource> = {
|
export type Filter<T extends Resource = Resource> = {
|
||||||
type: string;
|
type: string;
|
||||||
id: string;
|
id: string;
|
||||||
displayName: string;
|
displayName?: string;
|
||||||
filter: (resource: T) => boolean;
|
filter: (resource: T) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validStatusDisplayName = {
|
||||||
|
[ValidStatus.VALID]: translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.valid",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "状态: 通过",
|
||||||
|
}),
|
||||||
|
[ValidStatus.INVALID]: translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.invalid",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "状态: 未通过",
|
||||||
|
}),
|
||||||
|
[ValidStatus.SKIP]: translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.skip",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "状态: 跳过",
|
||||||
|
}),
|
||||||
|
[ValidStatus.MISSING]: translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.missing",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "状态: 缺失",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validStatusFilter = <T extends Resource = Resource>(
|
||||||
|
validStatus: ValidStatus
|
||||||
|
): Filter<T> => ({
|
||||||
|
type: "validStatus",
|
||||||
|
id: `validStatus-${validStatus}`,
|
||||||
|
displayName: validStatusDisplayName[validStatus],
|
||||||
|
filter: (resource: Resource): boolean =>
|
||||||
|
resource.resourceType === "plugin"
|
||||||
|
? getValidStatus(resource) === validStatus
|
||||||
|
: true,
|
||||||
|
});
|
||||||
|
|
||||||
export const tagFilter = <T extends Resource = Resource>(
|
export const tagFilter = <T extends Resource = Resource>(
|
||||||
tag: string
|
tag: string
|
||||||
): Filter<T> => ({
|
): Filter<T> => ({
|
||||||
@ -27,6 +65,7 @@ export const tagFilter = <T extends Resource = Resource>(
|
|||||||
filter: (resource: Resource): boolean =>
|
filter: (resource: Resource): boolean =>
|
||||||
resource.tags.map((tag) => tag.label).includes(tag),
|
resource.tags.map((tag) => tag.label).includes(tag),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const officialFilter = <T extends Resource = Resource>(
|
export const officialFilter = <T extends Resource = Resource>(
|
||||||
official: boolean = true
|
official: boolean = true
|
||||||
): Filter<T> => ({
|
): Filter<T> => ({
|
||||||
@ -39,6 +78,7 @@ export const officialFilter = <T extends Resource = Resource>(
|
|||||||
}).split("|")[Number(official)],
|
}).split("|")[Number(official)],
|
||||||
filter: (resource: Resource): boolean => resource.is_official === official,
|
filter: (resource: Resource): boolean => resource.is_official === official,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const authorFilter = <T extends Resource = Resource>(
|
export const authorFilter = <T extends Resource = Resource>(
|
||||||
author: string
|
author: string
|
||||||
): Filter<T> => ({
|
): Filter<T> => ({
|
||||||
@ -54,6 +94,7 @@ export const authorFilter = <T extends Resource = Resource>(
|
|||||||
),
|
),
|
||||||
filter: (resource: Resource): boolean => resource.author === author,
|
filter: (resource: Resource): boolean => resource.author === author,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const queryFilter = <T extends Resource = Resource>(
|
export const queryFilter = <T extends Resource = Resource>(
|
||||||
query: string
|
query: string
|
||||||
): Filter<T> => ({
|
): Filter<T> => ({
|
||||||
|
4
website/src/libs/sorter.ts
Normal file
4
website/src/libs/sorter.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export enum SortMode {
|
||||||
|
Default,
|
||||||
|
UpdateDesc,
|
||||||
|
}
|
@ -1,5 +1,13 @@
|
|||||||
import { authorFilter, tagFilter, type Filter } from "./filter";
|
import { translate } from "@docusaurus/Translate";
|
||||||
|
|
||||||
|
import {
|
||||||
|
authorFilter,
|
||||||
|
tagFilter,
|
||||||
|
validStatusFilter,
|
||||||
|
type Filter,
|
||||||
|
} from "./filter";
|
||||||
import type { Resource } from "./store";
|
import type { Resource } from "./store";
|
||||||
|
import { ValidStatus } from "./valid";
|
||||||
|
|
||||||
import type { Filter as FilterTool } from "@/components/Store/Toolbar";
|
import type { Filter as FilterTool } from "@/components/Store/Toolbar";
|
||||||
|
|
||||||
@ -38,7 +46,41 @@ export function useToolbar<T extends Resource = Resource>({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validateStatusFilterMapping: Record<string, ValidStatus> = {
|
||||||
|
[translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.valid",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "通过",
|
||||||
|
})]: ValidStatus.VALID,
|
||||||
|
[translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.invalid",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "未通过",
|
||||||
|
})]: ValidStatus.INVALID,
|
||||||
|
[translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.skip",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "跳过",
|
||||||
|
})]: ValidStatus.SKIP,
|
||||||
|
[translate({
|
||||||
|
id: "pages.store.filter.validateStatusDisplayName.missing",
|
||||||
|
description: "The display name of validateStatus filter",
|
||||||
|
message: "缺失",
|
||||||
|
})]: ValidStatus.MISSING,
|
||||||
|
};
|
||||||
|
|
||||||
|
const validStatusFilterTool: FilterTool = {
|
||||||
|
label: "状态",
|
||||||
|
icon: ["fas", "plug"],
|
||||||
|
choices: Object.keys(validateStatusFilterMapping),
|
||||||
|
onSubmit: (type: string) => {
|
||||||
|
const validStatus = validateStatusFilterMapping[type];
|
||||||
|
if (!validStatus) return;
|
||||||
|
addFilter(validStatusFilter(validStatus));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: [authorFilterTool, tagFilterTool],
|
filters: [authorFilterTool, tagFilterTool, validStatusFilterTool],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import Layout from "@theme/Layout";
|
|||||||
|
|
||||||
import HomeContent from "@/components/Home";
|
import HomeContent from "@/components/Home";
|
||||||
|
|
||||||
export default function Homepage(): JSX.Element {
|
export default function Homepage(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<HomeContent />
|
<HomeContent />
|
||||||
|
@ -5,7 +5,7 @@ import { translate } from "@docusaurus/Translate";
|
|||||||
import AdapterPageContent from "@/components/Store/Content/Adapter";
|
import AdapterPageContent from "@/components/Store/Content/Adapter";
|
||||||
import StoreLayout from "@/components/Store/Layout";
|
import StoreLayout from "@/components/Store/Layout";
|
||||||
|
|
||||||
export default function StoreAdapters(): JSX.Element {
|
export default function StoreAdapters(): React.ReactNode {
|
||||||
const title = translate({
|
const title = translate({
|
||||||
id: "pages.store.adapter.title",
|
id: "pages.store.adapter.title",
|
||||||
message: "适配器商店",
|
message: "适配器商店",
|
||||||
|
@ -5,7 +5,7 @@ import { translate } from "@docusaurus/Translate";
|
|||||||
import BotPageContent from "@/components/Store/Content/Bot";
|
import BotPageContent from "@/components/Store/Content/Bot";
|
||||||
import StoreLayout from "@/components/Store/Layout";
|
import StoreLayout from "@/components/Store/Layout";
|
||||||
|
|
||||||
export default function StoreBots(): JSX.Element {
|
export default function StoreBots(): React.ReactNode {
|
||||||
const title = translate({
|
const title = translate({
|
||||||
id: "pages.store.bot.title",
|
id: "pages.store.bot.title",
|
||||||
message: "机器人商店",
|
message: "机器人商店",
|
||||||
|
@ -5,7 +5,7 @@ import { translate } from "@docusaurus/Translate";
|
|||||||
import DriverPageContent from "@/components/Store/Content/Driver";
|
import DriverPageContent from "@/components/Store/Content/Driver";
|
||||||
import StoreLayout from "@/components/Store/Layout";
|
import StoreLayout from "@/components/Store/Layout";
|
||||||
|
|
||||||
export default function StoreDrivers(): JSX.Element {
|
export default function StoreDrivers(): React.ReactNode {
|
||||||
const title = translate({
|
const title = translate({
|
||||||
id: "pages.store.driver.title",
|
id: "pages.store.driver.title",
|
||||||
message: "驱动器商店",
|
message: "驱动器商店",
|
||||||
|
@ -2,6 +2,6 @@ import React from "react";
|
|||||||
|
|
||||||
import { Redirect } from "@docusaurus/router";
|
import { Redirect } from "@docusaurus/router";
|
||||||
|
|
||||||
export default function Store(): JSX.Element {
|
export default function Store(): React.ReactNode {
|
||||||
return <Redirect to="/store/plugins" />;
|
return <Redirect to="/store/plugins" />;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { translate } from "@docusaurus/Translate";
|
|||||||
import PluginPageContent from "@/components/Store/Content/Plugin";
|
import PluginPageContent from "@/components/Store/Content/Plugin";
|
||||||
import StoreLayout from "@/components/Store/Layout";
|
import StoreLayout from "@/components/Store/Layout";
|
||||||
|
|
||||||
export default function StorePlugins(): JSX.Element {
|
export default function StorePlugins(): React.ReactNode {
|
||||||
const title = translate({
|
const title = translate({
|
||||||
id: "pages.store.plugin.title",
|
id: "pages.store.plugin.title",
|
||||||
message: "插件商店",
|
message: "插件商店",
|
||||||
|
@ -2,7 +2,7 @@ import React, { type ComponentProps } from "react";
|
|||||||
|
|
||||||
export interface Props extends Omit<ComponentProps<"svg">, "viewBox"> {}
|
export interface Props extends Omit<ComponentProps<"svg">, "viewBox"> {}
|
||||||
|
|
||||||
export default function IconCloudflare(props: Props): JSX.Element {
|
export default function IconCloudflare(props: Props): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 651.29 94.76"
|
viewBox="0 0 651.29 94.76"
|
||||||
|
@ -2,7 +2,7 @@ import React, { type ComponentProps } from "react";
|
|||||||
|
|
||||||
export interface Props extends Omit<ComponentProps<"svg">, "viewBox"> {}
|
export interface Props extends Omit<ComponentProps<"svg">, "viewBox"> {}
|
||||||
|
|
||||||
export default function IconNetlify(props: Props): JSX.Element {
|
export default function IconNetlify(props: Props): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 256 105" xmlns="http://www.w3.org/2000/svg" {...props}>
|
<svg viewBox="0 0 256 105" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
<g clipPath="url(#clip0_236_25)">
|
<g clipPath="url(#clip0_236_25)">
|
||||||
|
@ -9,7 +9,7 @@ import "./styles.css";
|
|||||||
export default function TOCContainer({
|
export default function TOCContainer({
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: Props): JSX.Element {
|
}: Props): React.ReactNode {
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const isClient = windowSize !== "ssr";
|
const isClient = windowSize !== "ssr";
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user