📝 Docs: 商店头像 skeleton (#3362)

This commit is contained in:
StarHeart
2025-03-09 21:18:32 +08:00
committed by GitHub
parent ecab229133
commit 09343c332e
34 changed files with 623 additions and 282 deletions

View File

@ -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" />;
}

View File

@ -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";

View File

@ -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>

View File

@ -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 = () =>

View File

@ -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 {

View File

@ -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, "&nbsp;"),
}}
></div>
/>
</div>
);
}

View File

@ -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));

View 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>
);
}

View File

@ -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}

View File

@ -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>

View File

@ -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 & {

View File

@ -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;

View File

@ -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 = {

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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;

View File

@ -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,