mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-09-06 03:56:45 +00:00
📝 Docs: 商店头像 skeleton (#3362)
This commit is contained in:
@ -32,5 +32,5 @@ export default function AsciinemaContainer({
|
||||
AsciinemaPlayer.create(url, ref.current, options);
|
||||
}, [url, options]);
|
||||
|
||||
return <div ref={ref} className="not-prose ap-container"></div>;
|
||||
return <div ref={ref} className="not-prose ap-container" />;
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
import React from "react";
|
||||
|
||||
import BrowserOnly from "@docusaurus/BrowserOnly";
|
||||
|
||||
import "asciinema-player/dist/bundle/asciinema-player.css";
|
||||
|
||||
import type { Props } from "./container";
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
export type { Props } from "./container";
|
||||
|
@ -8,7 +8,8 @@ import { ChromePicker, type ColorResult } from "react-color";
|
||||
import "./styles.css";
|
||||
|
||||
import TagComponent from "@/components/Tag";
|
||||
import { Tag as TagType } from "@/types/tag";
|
||||
|
||||
import type { Tag as TagType } from "@/types/tag";
|
||||
|
||||
export type Props = {
|
||||
allowTags: TagType[];
|
||||
@ -103,7 +104,7 @@ export default function TagFormItem({
|
||||
<ChromePicker
|
||||
className="my-4 fix-input-color"
|
||||
color={color}
|
||||
disableAlpha={true}
|
||||
disableAlpha
|
||||
onChangeComplete={onChangeColor}
|
||||
/>
|
||||
</div>
|
||||
|
@ -4,10 +4,13 @@ import clsx from "clsx";
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
|
||||
import type { Resource } from "@/libs/store";
|
||||
import { fetchRegistryData } from "@/libs/store";
|
||||
|
||||
import TagFormItem from "./Items/Tag";
|
||||
|
||||
import { fetchRegistryData, Resource } from "@/libs/store";
|
||||
import { Tag as TagType } from "@/types/tag";
|
||||
import type { Tag as TagType } from "@/types/tag";
|
||||
|
||||
export type FormItemData = {
|
||||
type: string;
|
||||
@ -62,8 +65,8 @@ export function Form({
|
||||
(item) => item.name
|
||||
);
|
||||
if (currentStepNames.every((name) => result[name]))
|
||||
setCurrentStep(currentStep + 1);
|
||||
else return;
|
||||
{setCurrentStep(currentStep + 1);}
|
||||
else {}
|
||||
};
|
||||
const onPrev = () => currentStep > 0 && setCurrentStep(currentStep - 1);
|
||||
const onNext = () =>
|
||||
|
@ -2,6 +2,7 @@ import React from "react";
|
||||
|
||||
import HomeFeatures from "./Feature";
|
||||
import HomeHero from "./Hero";
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
export default function HomeContent(): React.ReactNode {
|
||||
|
@ -7,6 +7,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNonepressThemeConfig } from "@nullbot/docusaurus-theme-nonepress/client";
|
||||
|
||||
import ThemedImage from "@theme/ThemedImage";
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
export type Message = {
|
||||
@ -54,7 +55,7 @@ function MessageBox({
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: msg.replace(/\n/g, "<br/>").replace(/ /g, " "),
|
||||
}}
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import React, { useCallback } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import type { usePagination } from "react-use-pagination";
|
||||
|
||||
import "./styles.css";
|
||||
@ -66,7 +67,7 @@ export default function Paginate({
|
||||
const even = MAX_LENGTH % 2 === 0 ? 1 : 0;
|
||||
const left = Math.floor(MAX_LENGTH / 2);
|
||||
const right = totalPages - left + even + 1;
|
||||
currentPage = currentPage + 1;
|
||||
currentPage += 1;
|
||||
|
||||
if (totalPages <= MAX_LENGTH) {
|
||||
pages.push(...range(1, totalPages));
|
||||
|
43
website/src/components/Resource/Avatar/index.tsx
Normal file
43
website/src/components/Resource/Avatar/index.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { useState } from "react";
|
||||
import Link from "@docusaurus/Link";
|
||||
import clsx from "clsx";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
authorLink: string;
|
||||
authorAvatar: string;
|
||||
}
|
||||
|
||||
export default function Avatar({ authorLink, authorAvatar, className }: Props) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const onLoad = () => setLoaded(true);
|
||||
|
||||
return (
|
||||
<div className="avatar">
|
||||
<div className={className}>
|
||||
<Link href={authorLink}>
|
||||
<div className="relative w-full h-full">
|
||||
{!loaded && (
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute inset-0 rounded-full bg-gray-200",
|
||||
"animate-pulse"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<img
|
||||
src={authorAvatar}
|
||||
onLoad={onLoad}
|
||||
className={clsx(
|
||||
"w-full h-full rounded-full object-cover",
|
||||
"transition-opacity duration-300",
|
||||
loaded ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
alt="Avatar"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -5,9 +5,11 @@ import clsx from "clsx";
|
||||
import Link from "@docusaurus/Link";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import Avatar from "@/components/Resource/Avatar";
|
||||
import Tag from "@/components/Resource/Tag";
|
||||
import ValidStatus from "@/components/Resource/ValidStatus";
|
||||
import type { Resource } from "@/libs/store";
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
export type Props = {
|
||||
@ -73,7 +75,7 @@ export default function ResourceCard({
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="divider resource-card-footer-divider"></div>
|
||||
<div className="divider resource-card-footer-divider" />
|
||||
<div className="resource-card-footer-info">
|
||||
<div className="resource-card-footer-group">
|
||||
<Link href={resource.homepage}>
|
||||
@ -99,13 +101,12 @@ export default function ResourceCard({
|
||||
)}
|
||||
</div>
|
||||
<div className="resource-card-footer-group">
|
||||
<div className="avatar">
|
||||
<div className="resource-card-footer-avatar">
|
||||
<Link href={authorLink}>
|
||||
<img src={authorAvatar} key={resource.author} />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<Avatar
|
||||
className="resource-card-footer-avatar"
|
||||
key={resource.author}
|
||||
authorAvatar={authorAvatar}
|
||||
authorLink={authorLink}
|
||||
/>
|
||||
<span
|
||||
className="resource-card-footer-author"
|
||||
onClick={onAuthorClick}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
// @ts-expect-error: we need to make package have type: module
|
||||
import copy from "copy-text-to-clipboard";
|
||||
|
||||
import { PyPIData } from "./types";
|
||||
|
||||
import Tag from "@/components/Resource/Tag";
|
||||
import ValidStatus from "@/components/Resource/ValidStatus";
|
||||
import type { Resource } from "@/libs/store";
|
||||
|
||||
import type { PyPIData } from "./types";
|
||||
|
||||
import Avatar from "../Avatar";
|
||||
import "./styles.css";
|
||||
|
||||
export type Props = {
|
||||
@ -97,11 +97,13 @@ export default function ResourceDetailCard({ resource }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchingTasks: Promise<void>[] = [];
|
||||
if (resource.resourceType === "bot" || resource.resourceType === "driver")
|
||||
if (resource.resourceType === "bot" || resource.resourceType === "driver") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource.project_link)
|
||||
if (resource.project_link) {
|
||||
fetchingTasks.push(fetchPypiProject(resource.project_link));
|
||||
}
|
||||
|
||||
Promise.all(fetchingTasks);
|
||||
}, [resource]);
|
||||
@ -115,10 +117,11 @@ export default function ResourceDetailCard({ resource }: Props) {
|
||||
return (
|
||||
<>
|
||||
<div className="detail-card-header">
|
||||
<img
|
||||
src={authorAvatar}
|
||||
<Avatar
|
||||
className="detail-card-avatar"
|
||||
decoding="async"
|
||||
key={resource.author}
|
||||
authorLink={authorLink}
|
||||
authorAvatar={authorAvatar}
|
||||
/>
|
||||
<div className="detail-card-title">
|
||||
<span className="detail-card-title-main flex items-center gap-x-1">
|
||||
@ -152,7 +155,7 @@ export default function ResourceDetailCard({ resource }: Props) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divider detail-card-header-divider"></div>
|
||||
<div className="divider detail-card-header-divider" />
|
||||
<div className="detail-card-body">
|
||||
<div className="detail-card-body-left">
|
||||
<span className="h-full">{resource.desc}</span>
|
||||
|
@ -3,7 +3,10 @@ import React from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { pickTextColor } from "@/libs/color";
|
||||
|
||||
import type { Tag } from "@/types/tag";
|
||||
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
export type Props = Tag & {
|
||||
|
@ -2,17 +2,18 @@ import React from "react";
|
||||
|
||||
import clsx from "clsx";
|
||||
|
||||
import type { IconName } from "@fortawesome/fontawesome-common-types";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Resource } from "@/libs/store";
|
||||
import type { Resource } from "@/libs/store";
|
||||
import { ValidStatus } from "@/libs/valid";
|
||||
|
||||
import type { IconName } from "@fortawesome/fontawesome-common-types";
|
||||
|
||||
export const getValidStatus = (resource: Resource) => {
|
||||
switch (resource.resourceType) {
|
||||
case "plugin":
|
||||
if (resource.skip_test) return ValidStatus.SKIP;
|
||||
if (resource.valid) return ValidStatus.VALID;
|
||||
if (resource.skip_test) {return ValidStatus.SKIP;}
|
||||
if (resource.valid) {return ValidStatus.VALID;}
|
||||
return ValidStatus.INVALID;
|
||||
default:
|
||||
return ValidStatus.MISSING;
|
||||
|
@ -4,6 +4,7 @@ import clsx from "clsx";
|
||||
|
||||
import { translate } from "@docusaurus/Translate";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
export type Props = {
|
||||
|
@ -4,7 +4,6 @@ import Translate from "@docusaurus/Translate";
|
||||
import { usePagination } from "react-use-pagination";
|
||||
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
||||
import AdapterForm from "@/components/Form/Adapter";
|
||||
import Modal from "@/components/Modal";
|
||||
import Paginate from "@/components/Paginate";
|
||||
@ -16,6 +15,7 @@ import { authorFilter, tagFilter } from "@/libs/filter";
|
||||
import { useSearchControl } from "@/libs/search";
|
||||
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
||||
import { useToolbar } from "@/libs/toolbar";
|
||||
|
||||
import type { Adapter } from "@/types/adapter";
|
||||
|
||||
export default function AdapterPage(): React.ReactNode {
|
||||
@ -146,7 +146,7 @@ export default function AdapterPage(): React.ReactNode {
|
||||
</Admonition>
|
||||
) : loading ? (
|
||||
<p className="store-loading-container">
|
||||
<span className="loading loading-dots loading-lg store-loading"></span>
|
||||
<span className="loading loading-dots loading-lg store-loading" />
|
||||
</p>
|
||||
) : (
|
||||
<div className="store-container">
|
||||
|
@ -4,7 +4,6 @@ import Translate from "@docusaurus/Translate";
|
||||
import { usePagination } from "react-use-pagination";
|
||||
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
||||
import BotForm from "@/components/Form/Bot";
|
||||
import Modal from "@/components/Modal";
|
||||
import Paginate from "@/components/Paginate";
|
||||
@ -15,6 +14,7 @@ import { authorFilter, tagFilter } from "@/libs/filter";
|
||||
import { useSearchControl } from "@/libs/search";
|
||||
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
||||
import { useToolbar } from "@/libs/toolbar";
|
||||
|
||||
import type { Bot } from "@/types/bot";
|
||||
|
||||
export default function PluginPage(): React.ReactNode {
|
||||
@ -138,7 +138,7 @@ export default function PluginPage(): React.ReactNode {
|
||||
</Admonition>
|
||||
) : loading ? (
|
||||
<p className="store-loading-container">
|
||||
<span className="loading loading-dots loading-lg store-loading"></span>
|
||||
<span className="loading loading-dots loading-lg store-loading" />
|
||||
</p>
|
||||
) : (
|
||||
<div className="store-container">
|
||||
|
@ -4,7 +4,6 @@ import Translate from "@docusaurus/Translate";
|
||||
import { usePagination } from "react-use-pagination";
|
||||
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
||||
import Modal from "@/components/Modal";
|
||||
import Paginate from "@/components/Paginate";
|
||||
import ResourceCard from "@/components/Resource/Card";
|
||||
@ -13,6 +12,7 @@ import Searcher from "@/components/Searcher";
|
||||
import { authorFilter, tagFilter } from "@/libs/filter";
|
||||
import { useSearchControl } from "@/libs/search";
|
||||
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
||||
|
||||
import type { Driver } from "@/types/driver";
|
||||
|
||||
export default function DriverPage(): React.ReactNode {
|
||||
@ -123,7 +123,7 @@ export default function DriverPage(): React.ReactNode {
|
||||
</Admonition>
|
||||
) : loading ? (
|
||||
<p className="store-loading-container">
|
||||
<span className="loading loading-dots loading-lg store-loading"></span>
|
||||
<span className="loading loading-dots loading-lg store-loading" />
|
||||
</p>
|
||||
) : (
|
||||
<div className="store-container">
|
||||
|
@ -4,7 +4,6 @@ import Translate, { translate } from "@docusaurus/Translate";
|
||||
import { usePagination } from "react-use-pagination";
|
||||
|
||||
import Admonition from "@theme/Admonition";
|
||||
|
||||
import PluginForm from "@/components/Form/Plugin";
|
||||
import Modal from "@/components/Modal";
|
||||
import Paginate from "@/components/Paginate";
|
||||
@ -20,6 +19,7 @@ import { useSearchControl } from "@/libs/search";
|
||||
import { SortMode } from "@/libs/sorter";
|
||||
import { fetchRegistryData, loadFailedTitle } from "@/libs/store";
|
||||
import { useToolbar } from "@/libs/toolbar";
|
||||
|
||||
import type { Plugin } from "@/types/plugin";
|
||||
|
||||
export default function PluginPage(): React.ReactNode {
|
||||
@ -149,7 +149,7 @@ export default function PluginPage(): React.ReactNode {
|
||||
<Translate
|
||||
id="pages.store.plugin.searchInfo"
|
||||
description="Plugins search info of the plugin store page"
|
||||
values={{ pluginCount, filteredPluginCount: filteredPluginCount }}
|
||||
values={{ pluginCount, filteredPluginCount }}
|
||||
>
|
||||
{"当前共有 {filteredPluginCount} / {pluginCount} 个插件"}
|
||||
</Translate>
|
||||
@ -180,7 +180,7 @@ export default function PluginPage(): React.ReactNode {
|
||||
</Admonition>
|
||||
) : loading ? (
|
||||
<p className="store-loading-container">
|
||||
<span className="loading loading-dots loading-lg store-loading"></span>
|
||||
<span className="loading loading-dots loading-lg store-loading" />
|
||||
</p>
|
||||
) : (
|
||||
<div className="store-container">
|
||||
|
@ -2,9 +2,10 @@ import React, { useState } from "react";
|
||||
|
||||
import clsx from "clsx";
|
||||
|
||||
import type { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import type { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
export type Filter = {
|
||||
label: string;
|
||||
icon: IconProp;
|
||||
|
@ -5,7 +5,8 @@ import clsx from "clsx";
|
||||
import "./styles.css";
|
||||
|
||||
import { pickTextColor } from "@/libs/color";
|
||||
import { Tag as TagType } from "@/types/tag";
|
||||
|
||||
import type { Tag as TagType } from "@/types/tag";
|
||||
|
||||
export default function Tag({
|
||||
label,
|
||||
|
Reference in New Issue
Block a user