feat: add new spinner component

This commit is contained in:
2024-04-13 15:01:27 +07:00
parent 89f577fbef
commit ed6aca41ea
29 changed files with 177 additions and 102 deletions

View File

@@ -60,3 +60,63 @@ input::-ms-clear {
::-webkit-input-placeholder {
line-height: normal;
}
.spinner-leaf {
position: absolute;
top: 0;
left: calc(50% - 12.5% / 2);
width: 12.5%;
height: 100%;
animation: spinner-leaf-fade 800ms linear infinite;
&::before {
content: "";
display: block;
width: 100%;
height: 30%;
background-color: currentColor;
@apply rounded;
}
&:where(:nth-child(1)) {
transform: rotate(0deg);
animation-delay: -800ms;
}
&:where(:nth-child(2)) {
transform: rotate(45deg);
animation-delay: -700ms;
}
&:where(:nth-child(3)) {
transform: rotate(90deg);
animation-delay: -600ms;
}
&:where(:nth-child(4)) {
transform: rotate(135deg);
animation-delay: -500ms;
}
&:where(:nth-child(5)) {
transform: rotate(180deg);
animation-delay: -400ms;
}
&:where(:nth-child(6)) {
transform: rotate(225deg);
animation-delay: -300ms;
}
&:where(:nth-child(7)) {
transform: rotate(270deg);
animation-delay: -200ms;
}
&:where(:nth-child(8)) {
transform: rotate(315deg);
animation-delay: -100ms;
}
}
@keyframes spinner-leaf-fade {
from {
opacity: 1;
}
to {
opacity: 0.25;
}
}

View File

@@ -1,4 +1,4 @@
import { LoaderIcon } from "@lume/icons";
import { Spinner } from "@lume/ui";
import { cn } from "@lume/utils";
import { useRouteContext } from "@tanstack/react-router";
import { Dispatch, ReactNode, SetStateAction, useState } from "react";
@@ -36,7 +36,7 @@ export function AvatarUploader({
onClick={() => uploadAvatar()}
className={cn("size-4", className)}
>
{loading ? <LoaderIcon className="size-4 animate-spin" /> : children}
{loading ? <Spinner className="size-4" /> : children}
</button>
);
}

View File

@@ -1,7 +1,7 @@
import { useEffect, useRef } from "react";
import { LumeColumn } from "@lume/types";
import { invoke } from "@tauri-apps/api/core";
import { LoaderIcon } from "@lume/icons";
import { Spinner } from "@lume/ui";
export function Col({
column,
@@ -68,7 +68,7 @@ export function Col({
{column.label !== "open" ? (
<div className="w-full h-full flex items-center justify-center rounded-xl flex-col bg-black/5 dark:bg-white/5 backdrop-blur-lg">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
</div>
) : null}

View File

@@ -1,7 +1,8 @@
import { Col } from "@/components/col";
import { Toolbar } from "@/components/toolbar";
import { ArrowLeftIcon, ArrowRightIcon, LoaderIcon } from "@lume/icons";
import { ArrowLeftIcon, ArrowRightIcon } from "@lume/icons";
import { EventColumns, LumeColumn } from "@lume/types";
import { Spinner } from "@lume/ui";
import { createFileRoute } from "@tanstack/react-router";
import { listen } from "@tauri-apps/api/event";
import { resolveResource } from "@tauri-apps/api/path";
@@ -148,7 +149,7 @@ function Pending() {
return (
<div className="flex h-full w-full items-center justify-center">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
</div>
);

View File

@@ -1,9 +1,9 @@
import { LoaderIcon } from "@lume/icons";
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
import { type Ark } from "@lume/ark";
import { type QueryClient } from "@tanstack/react-query";
import { type Platform } from "@tauri-apps/plugin-os";
import { Account, Interests, Settings } from "@lume/types";
import { Spinner } from "@lume/ui";
interface RouterContext {
ark: Ark;
@@ -25,7 +25,7 @@ function Pending() {
return (
<div className="flex h-screen w-screen flex-col items-center justify-center">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
</div>
);

View File

@@ -1,8 +1,8 @@
import { RepostNote } from "@/components/repost";
import { TextNote } from "@/components/text";
import { LoaderIcon, ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ColumnRouteSearch, Event, Kind } from "@lume/types";
import { Column } from "@lume/ui";
import { Column, Spinner } from "@lume/ui";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Link, createFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
@@ -53,7 +53,7 @@ export function Screen() {
<Column.Content>
{isLoading ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</div>
) : !data.length ? (
<Empty />
@@ -71,7 +71,7 @@ export function Screen() {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />

View File

@@ -1,6 +1,7 @@
import { AvatarUploader } from "@/components/avatarUploader";
import { LoaderIcon, PlusIcon } from "@lume/icons";
import { PlusIcon } from "@lume/icons";
import { Metadata } from "@lume/types";
import { Spinner } from "@lume/ui";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { useForm } from "react-hook-form";
@@ -134,11 +135,7 @@ function Screen() {
type="submit"
className="mt-3 inline-flex h-11 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600 disabled:opacity-50"
>
{loading ? (
<LoaderIcon className="size-4 animate-spin" />
) : (
t("global.continue")
)}
{loading ? <Spinner /> : t("global.continue")}
</button>
</form>
</div>

View File

@@ -1,4 +1,4 @@
import { LoaderIcon } from "@lume/icons";
import { Spinner } from "@lume/ui";
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { toast } from "sonner";
@@ -82,7 +82,7 @@ function Screen() {
disabled={loading}
className="mt-3 inline-flex h-11 w-full shrink-0 items-center justify-center rounded-lg bg-blue-500 font-semibold text-white hover:bg-blue-600 disabled:opacity-50"
>
{loading ? <LoaderIcon className="size-4 animate-spin" /> : "Login"}
{loading ? <Spinner /> : "Login"}
</button>
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { LaurelIcon, LoaderIcon } from "@lume/icons";
import { LaurelIcon } from "@lume/icons";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useTranslation } from "react-i18next";
import * as Switch from "@radix-ui/react-switch";
@@ -9,6 +9,7 @@ import {
requestPermission,
} from "@tauri-apps/plugin-notification";
import { toast } from "sonner";
import { Spinner } from "@lume/ui";
export const Route = createFileRoute("/auth/settings")({
validateSearch: (search: Record<string, string>): AppRouteSearch => {
@@ -181,7 +182,7 @@ function Pending() {
return (
<div className="flex h-full w-full items-center justify-center">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
</div>
);

View File

@@ -1,4 +1,4 @@
import { AddMediaIcon, LoaderIcon } from "@lume/icons";
import { AddMediaIcon } from "@lume/icons";
import { cn, insertImage, isImagePath } from "@lume/utils";
import { useEffect, useState } from "react";
import { useSlateStatic } from "slate-react";
@@ -6,6 +6,7 @@ import { toast } from "sonner";
import { getCurrent } from "@tauri-apps/api/window";
import { UnlistenFn } from "@tauri-apps/api/event";
import { useRouteContext } from "@tanstack/react-router";
import { Spinner } from "@lume/ui";
export function MediaButton({ className }: { className?: string }) {
const { ark } = useRouteContext({ strict: false });
@@ -69,7 +70,7 @@ export function MediaButton({ className }: { className?: string }) {
className={cn("inline-flex items-center justify-center", className)}
>
{loading ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<AddMediaIcon className="size-5" />
)}

View File

@@ -31,7 +31,7 @@ import {
Editable,
} from "slate-react";
import { Contact } from "@lume/types";
import { User } from "@lume/ui";
import { Spinner, User } from "@lume/ui";
import { nip19 } from "nostr-tools";
import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
import { invoke } from "@tauri-apps/api/core";
@@ -212,11 +212,7 @@ function Screen() {
onClick={publish}
className="inline-flex h-9 w-24 items-center justify-center rounded-full bg-blue-500 px-3 font-medium text-white hover:bg-blue-600"
>
{loading ? (
<LoaderIcon className="size-5 animate-spin" />
) : (
t("global.post")
)}
{loading ? <Spinner className="size-5" /> : t("global.post")}
</button>
</div>
<div className="flex h-full min-h-0 w-full">
@@ -280,7 +276,7 @@ function Pending() {
className="flex h-full w-full items-center justify-center gap-2.5"
>
<button type="button" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
<p>Loading cache...</p>
</div>

View File

@@ -1,6 +1,5 @@
import { useEvent } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { Box, Container, Note, User } from "@lume/ui";
import { Box, Container, Note, Spinner, User } from "@lume/ui";
import { createLazyFileRoute } from "@tanstack/react-router";
import { ReplyList } from "./-components/replyList";
import { WindowVirtualizer } from "virtua";
@@ -17,7 +16,7 @@ function Event() {
if (isLoading) {
return (
<div className="flex h-full w-full items-center justify-center">
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5 animate-spin" />
</div>
);
}

View File

@@ -1,10 +1,10 @@
import { LoaderIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { EventWithReplies } from "@lume/types";
import { Reply } from "./reply";
import { useRouteContext } from "@tanstack/react-router";
import { Spinner } from "@lume/ui";
export function ReplyList({
eventId,
@@ -29,7 +29,7 @@ export function ReplyList({
<div className={cn("flex flex-col gap-3", className)}>
{!data ? (
<div className="mt-4 flex h-16 items-center justify-center p-3">
<LoaderIcon className="h-5 w-5 animate-spin" />
<Spinner className="size-5" />
</div>
) : data.length === 0 ? (
<div className="mt-4 flex w-full items-center justify-center">

View File

@@ -1,8 +1,8 @@
import { RepostNote } from "@/components/repost";
import { TextNote } from "@/components/text";
import { LoaderIcon, ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ColumnRouteSearch, Event, Kind } from "@lume/types";
import { Column } from "@lume/ui";
import { Column, Spinner } from "@lume/ui";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Link, createFileRoute, redirect } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
@@ -77,7 +77,7 @@ export function Screen() {
{isLoading ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
</div>
) : !data.length ? (
@@ -97,7 +97,7 @@ export function Screen() {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />

View File

@@ -1,8 +1,8 @@
import { RepostNote } from "@/components/repost";
import { TextNote } from "@/components/text";
import { LoaderIcon, ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ColumnRouteSearch, Event, Kind } from "@lume/types";
import { Column } from "@lume/ui";
import { Column, Spinner } from "@lume/ui";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Link, createFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
@@ -60,7 +60,7 @@ export function Screen() {
{isLoading ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</button>
</div>
) : !data.length ? (
@@ -79,7 +79,7 @@ export function Screen() {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />

View File

@@ -1,8 +1,8 @@
import { RepostNote } from "@/components/repost";
import { TextNote } from "@/components/text";
import { LoaderIcon, ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ColumnRouteSearch, Event, Kind } from "@lume/types";
import { Column } from "@lume/ui";
import { Column, Spinner } from "@lume/ui";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Link, createFileRoute, redirect } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
@@ -71,7 +71,7 @@ export function Screen() {
<Column.Content>
{isLoading ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</div>
) : !data.length ? (
<Empty />
@@ -89,7 +89,7 @@ export function Screen() {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />

View File

@@ -1,5 +1,5 @@
import { LoaderIcon, PlusIcon } from "@lume/icons";
import { User } from "@lume/ui";
import { PlusIcon } from "@lume/icons";
import { Spinner, User } from "@lume/ui";
import { Link } from "@tanstack/react-router";
import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
@@ -73,7 +73,7 @@ function Screen() {
<div className="flex items-center justify-center gap-6">
{loading ? (
<div className="inline-flex size-6 items-center justify-center">
<LoaderIcon className="size-6 animate-spin text-white" />
<Spinner className="size-6" />
</div>
) : (
<>

View File

@@ -1,8 +1,8 @@
import { RepostNote } from "@/components/repost";
import { TextNote } from "@/components/text";
import { LoaderIcon, ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ArrowRightCircleIcon, ArrowRightIcon } from "@lume/icons";
import { ColumnRouteSearch, Event, Kind } from "@lume/types";
import { Column } from "@lume/ui";
import { Column, Spinner } from "@lume/ui";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Link, createFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
@@ -59,9 +59,7 @@ export function Screen() {
<Column.Content>
{isLoading ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<button type="button" className="size-5" disabled>
<LoaderIcon className="size-5 animate-spin" />
</button>
<Spinner className="size-5" />
</div>
) : !data.length ? (
<Empty />
@@ -79,7 +77,7 @@ export function Screen() {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />

View File

@@ -1,11 +1,11 @@
import { RepostNote } from "@/components/repost";
import { TextNote } from "@/components/text";
import { LoaderIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { Await, createFileRoute } from "@tanstack/react-router";
import { Virtualizer } from "virtua";
import { defer } from "@tanstack/react-router";
import { Suspense } from "react";
import { Spinner } from "@lume/ui";
export const Route = createFileRoute("/trending/notes")({
loader: async ({ abortController }) => {
@@ -50,7 +50,7 @@ export function Screen() {
className="inline-flex items-center gap-2 text-sm font-medium"
disabled
>
<LoaderIcon className="animate-spin size-5" />
<Spinner className="size-5" />
Loading...
</button>
</div>

View File

@@ -1,5 +1,4 @@
import { LoaderIcon } from "@lume/icons";
import { User } from "@lume/ui";
import { Spinner, User } from "@lume/ui";
import { Await, defer } from "@tanstack/react-router";
import { createFileRoute } from "@tanstack/react-router";
import { Suspense } from "react";
@@ -34,7 +33,7 @@ export function Screen() {
className="inline-flex items-center gap-2 text-sm font-medium"
disabled
>
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
Loading...
</button>
</div>

View File

@@ -1,10 +1,11 @@
import { TextNote } from "@/components/text";
import { RepostNote } from "@/components/repost";
import { ArrowRightCircleIcon, InfoIcon, LoaderIcon } from "@lume/icons";
import { ArrowRightCircleIcon, InfoIcon } from "@lume/icons";
import { Event, Kind } from "@lume/types";
import { FETCH_LIMIT } from "@lume/utils";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useRouteContext } from "@tanstack/react-router";
import { Spinner } from "@lume/ui";
export function EventList({ id }: { id: string }) {
const { ark } = useRouteContext({ strict: false });
@@ -39,7 +40,7 @@ export function EventList({ id }: { id: string }) {
<div>
{isLoading ? (
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
</div>
) : !data.length ? (
<div className="flex items-center gap-2 rounded-xl bg-neutral-50 p-5 dark:bg-neutral-950">
@@ -58,7 +59,7 @@ export function EventList({ id }: { id: string }) {
className="inline-flex h-12 w-36 items-center justify-center gap-2 rounded-full bg-neutral-100 px-3 font-medium hover:bg-neutral-200 focus:outline-none dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
{isFetchingNextPage ? (
<LoaderIcon className="size-5 animate-spin" />
<Spinner className="size-5" />
) : (
<>
<ArrowRightCircleIcon className="size-5" />