mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
feat: add new spinner component
This commit is contained in:
parent
89f577fbef
commit
ed6aca41ea
@ -60,3 +60,63 @@ input::-ms-clear {
|
|||||||
::-webkit-input-placeholder {
|
::-webkit-input-placeholder {
|
||||||
line-height: normal;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LoaderIcon } from "@lume/icons";
|
import { Spinner } from "@lume/ui";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
import { Dispatch, ReactNode, SetStateAction, useState } from "react";
|
import { Dispatch, ReactNode, SetStateAction, useState } from "react";
|
||||||
@ -36,7 +36,7 @@ export function AvatarUploader({
|
|||||||
onClick={() => uploadAvatar()}
|
onClick={() => uploadAvatar()}
|
||||||
className={cn("size-4", className)}
|
className={cn("size-4", className)}
|
||||||
>
|
>
|
||||||
{loading ? <LoaderIcon className="size-4 animate-spin" /> : children}
|
{loading ? <Spinner className="size-4" /> : children}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { LumeColumn } from "@lume/types";
|
import { LumeColumn } from "@lume/types";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { LoaderIcon } from "@lume/icons";
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
export function Col({
|
export function Col({
|
||||||
column,
|
column,
|
||||||
@ -68,7 +68,7 @@ export function Col({
|
|||||||
{column.label !== "open" ? (
|
{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">
|
<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>
|
<button type="button" className="size-5" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Col } from "@/components/col";
|
import { Col } from "@/components/col";
|
||||||
import { Toolbar } from "@/components/toolbar";
|
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 { EventColumns, LumeColumn } from "@lume/types";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { resolveResource } from "@tauri-apps/api/path";
|
import { resolveResource } from "@tauri-apps/api/path";
|
||||||
@ -148,7 +149,7 @@ function Pending() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<button type="button" className="size-5" disabled>
|
<button type="button" className="size-5" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { LoaderIcon } from "@lume/icons";
|
|
||||||
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
|
||||||
import { type Ark } from "@lume/ark";
|
import { type Ark } from "@lume/ark";
|
||||||
import { type QueryClient } from "@tanstack/react-query";
|
import { type QueryClient } from "@tanstack/react-query";
|
||||||
import { type Platform } from "@tauri-apps/plugin-os";
|
import { type Platform } from "@tauri-apps/plugin-os";
|
||||||
import { Account, Interests, Settings } from "@lume/types";
|
import { Account, Interests, Settings } from "@lume/types";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
interface RouterContext {
|
interface RouterContext {
|
||||||
ark: Ark;
|
ark: Ark;
|
||||||
@ -25,7 +25,7 @@ function Pending() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-screen w-screen flex-col items-center justify-center">
|
<div className="flex h-screen w-screen flex-col items-center justify-center">
|
||||||
<button type="button" className="size-5" disabled>
|
<button type="button" className="size-5" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { RepostNote } from "@/components/repost";
|
import { RepostNote } from "@/components/repost";
|
||||||
import { TextNote } from "@/components/text";
|
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 { ColumnRouteSearch, Event, Kind } from "@lume/types";
|
||||||
import { Column } from "@lume/ui";
|
import { Column, Spinner } from "@lume/ui";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||||
import { Virtualizer } from "virtua";
|
import { Virtualizer } from "virtua";
|
||||||
@ -53,7 +53,7 @@ export function Screen() {
|
|||||||
<Column.Content>
|
<Column.Content>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<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>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<Empty />
|
<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"
|
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 ? (
|
{isFetchingNextPage ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowRightCircleIcon className="size-5" />
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { AvatarUploader } from "@/components/avatarUploader";
|
import { AvatarUploader } from "@/components/avatarUploader";
|
||||||
import { LoaderIcon, PlusIcon } from "@lume/icons";
|
import { PlusIcon } from "@lume/icons";
|
||||||
import { Metadata } from "@lume/types";
|
import { Metadata } from "@lume/types";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@ -134,11 +135,7 @@ function Screen() {
|
|||||||
type="submit"
|
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"
|
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 ? (
|
{loading ? <Spinner /> : t("global.continue")}
|
||||||
<LoaderIcon className="size-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
t("global.continue")
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LoaderIcon } from "@lume/icons";
|
import { Spinner } from "@lume/ui";
|
||||||
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@ -82,7 +82,7 @@ function Screen() {
|
|||||||
disabled={loading}
|
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"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LaurelIcon, LoaderIcon } from "@lume/icons";
|
import { LaurelIcon } from "@lume/icons";
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import * as Switch from "@radix-ui/react-switch";
|
import * as Switch from "@radix-ui/react-switch";
|
||||||
@ -9,6 +9,7 @@ import {
|
|||||||
requestPermission,
|
requestPermission,
|
||||||
} from "@tauri-apps/plugin-notification";
|
} from "@tauri-apps/plugin-notification";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
export const Route = createFileRoute("/auth/settings")({
|
export const Route = createFileRoute("/auth/settings")({
|
||||||
validateSearch: (search: Record<string, string>): AppRouteSearch => {
|
validateSearch: (search: Record<string, string>): AppRouteSearch => {
|
||||||
@ -181,7 +182,7 @@ function Pending() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<button type="button" className="size-5" disabled>
|
<button type="button" className="size-5" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AddMediaIcon, LoaderIcon } from "@lume/icons";
|
import { AddMediaIcon } from "@lume/icons";
|
||||||
import { cn, insertImage, isImagePath } from "@lume/utils";
|
import { cn, insertImage, isImagePath } from "@lume/utils";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useSlateStatic } from "slate-react";
|
import { useSlateStatic } from "slate-react";
|
||||||
@ -6,6 +6,7 @@ import { toast } from "sonner";
|
|||||||
import { getCurrent } from "@tauri-apps/api/window";
|
import { getCurrent } from "@tauri-apps/api/window";
|
||||||
import { UnlistenFn } from "@tauri-apps/api/event";
|
import { UnlistenFn } from "@tauri-apps/api/event";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
export function MediaButton({ className }: { className?: string }) {
|
export function MediaButton({ className }: { className?: string }) {
|
||||||
const { ark } = useRouteContext({ strict: false });
|
const { ark } = useRouteContext({ strict: false });
|
||||||
@ -69,7 +70,7 @@ export function MediaButton({ className }: { className?: string }) {
|
|||||||
className={cn("inline-flex items-center justify-center", className)}
|
className={cn("inline-flex items-center justify-center", className)}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<AddMediaIcon className="size-5" />
|
<AddMediaIcon className="size-5" />
|
||||||
)}
|
)}
|
||||||
|
@ -31,7 +31,7 @@ import {
|
|||||||
Editable,
|
Editable,
|
||||||
} from "slate-react";
|
} from "slate-react";
|
||||||
import { Contact } from "@lume/types";
|
import { Contact } from "@lume/types";
|
||||||
import { User } from "@lume/ui";
|
import { Spinner, User } from "@lume/ui";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
|
import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
@ -212,11 +212,7 @@ function Screen() {
|
|||||||
onClick={publish}
|
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"
|
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 ? (
|
{loading ? <Spinner className="size-5" /> : t("global.post")}
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
|
||||||
) : (
|
|
||||||
t("global.post")
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full min-h-0 w-full">
|
<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"
|
className="flex h-full w-full items-center justify-center gap-2.5"
|
||||||
>
|
>
|
||||||
<button type="button" disabled>
|
<button type="button" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
<p>Loading cache...</p>
|
<p>Loading cache...</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useEvent } from "@lume/ark";
|
import { useEvent } from "@lume/ark";
|
||||||
import { LoaderIcon } from "@lume/icons";
|
import { Box, Container, Note, Spinner, User } from "@lume/ui";
|
||||||
import { Box, Container, Note, User } from "@lume/ui";
|
|
||||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import { ReplyList } from "./-components/replyList";
|
import { ReplyList } from "./-components/replyList";
|
||||||
import { WindowVirtualizer } from "virtua";
|
import { WindowVirtualizer } from "virtua";
|
||||||
@ -17,7 +16,7 @@ function Event() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { LoaderIcon } from "@lume/icons";
|
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { EventWithReplies } from "@lume/types";
|
import { EventWithReplies } from "@lume/types";
|
||||||
import { Reply } from "./reply";
|
import { Reply } from "./reply";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
export function ReplyList({
|
export function ReplyList({
|
||||||
eventId,
|
eventId,
|
||||||
@ -29,7 +29,7 @@ export function ReplyList({
|
|||||||
<div className={cn("flex flex-col gap-3", className)}>
|
<div className={cn("flex flex-col gap-3", className)}>
|
||||||
{!data ? (
|
{!data ? (
|
||||||
<div className="mt-4 flex h-16 items-center justify-center p-3">
|
<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>
|
</div>
|
||||||
) : data.length === 0 ? (
|
) : data.length === 0 ? (
|
||||||
<div className="mt-4 flex w-full items-center justify-center">
|
<div className="mt-4 flex w-full items-center justify-center">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { RepostNote } from "@/components/repost";
|
import { RepostNote } from "@/components/repost";
|
||||||
import { TextNote } from "@/components/text";
|
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 { ColumnRouteSearch, Event, Kind } from "@lume/types";
|
||||||
import { Column } from "@lume/ui";
|
import { Column, Spinner } from "@lume/ui";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { Link, createFileRoute, redirect } from "@tanstack/react-router";
|
import { Link, createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
import { Virtualizer } from "virtua";
|
import { Virtualizer } from "virtua";
|
||||||
@ -77,7 +77,7 @@ export function Screen() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
||||||
<button type="button" className="size-5" disabled>
|
<button type="button" className="size-5" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !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"
|
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 ? (
|
{isFetchingNextPage ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowRightCircleIcon className="size-5" />
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { RepostNote } from "@/components/repost";
|
import { RepostNote } from "@/components/repost";
|
||||||
import { TextNote } from "@/components/text";
|
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 { ColumnRouteSearch, Event, Kind } from "@lume/types";
|
||||||
import { Column } from "@lume/ui";
|
import { Column, Spinner } from "@lume/ui";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||||
import { Virtualizer } from "virtua";
|
import { Virtualizer } from "virtua";
|
||||||
@ -60,7 +60,7 @@ export function Screen() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
||||||
<button type="button" className="size-5" disabled>
|
<button type="button" className="size-5" disabled>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !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"
|
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 ? (
|
{isFetchingNextPage ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowRightCircleIcon className="size-5" />
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { RepostNote } from "@/components/repost";
|
import { RepostNote } from "@/components/repost";
|
||||||
import { TextNote } from "@/components/text";
|
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 { ColumnRouteSearch, Event, Kind } from "@lume/types";
|
||||||
import { Column } from "@lume/ui";
|
import { Column, Spinner } from "@lume/ui";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { Link, createFileRoute, redirect } from "@tanstack/react-router";
|
import { Link, createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
import { Virtualizer } from "virtua";
|
import { Virtualizer } from "virtua";
|
||||||
@ -71,7 +71,7 @@ export function Screen() {
|
|||||||
<Column.Content>
|
<Column.Content>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<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>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<Empty />
|
<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"
|
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 ? (
|
{isFetchingNextPage ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowRightCircleIcon className="size-5" />
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { LoaderIcon, PlusIcon } from "@lume/icons";
|
import { PlusIcon } from "@lume/icons";
|
||||||
import { User } from "@lume/ui";
|
import { Spinner, User } from "@lume/ui";
|
||||||
import { Link } from "@tanstack/react-router";
|
import { Link } from "@tanstack/react-router";
|
||||||
import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router";
|
import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@ -73,7 +73,7 @@ function Screen() {
|
|||||||
<div className="flex items-center justify-center gap-6">
|
<div className="flex items-center justify-center gap-6">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="inline-flex size-6 items-center justify-center">
|
<div className="inline-flex size-6 items-center justify-center">
|
||||||
<LoaderIcon className="size-6 animate-spin text-white" />
|
<Spinner className="size-6" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { RepostNote } from "@/components/repost";
|
import { RepostNote } from "@/components/repost";
|
||||||
import { TextNote } from "@/components/text";
|
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 { ColumnRouteSearch, Event, Kind } from "@lume/types";
|
||||||
import { Column } from "@lume/ui";
|
import { Column, Spinner } from "@lume/ui";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||||
import { Virtualizer } from "virtua";
|
import { Virtualizer } from "virtua";
|
||||||
@ -59,9 +59,7 @@ export function Screen() {
|
|||||||
<Column.Content>
|
<Column.Content>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
||||||
<button type="button" className="size-5" disabled>
|
<Spinner className="size-5" />
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<Empty />
|
<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"
|
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 ? (
|
{isFetchingNextPage ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowRightCircleIcon className="size-5" />
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { RepostNote } from "@/components/repost";
|
import { RepostNote } from "@/components/repost";
|
||||||
import { TextNote } from "@/components/text";
|
import { TextNote } from "@/components/text";
|
||||||
import { LoaderIcon } from "@lume/icons";
|
|
||||||
import { Event, Kind } from "@lume/types";
|
import { Event, Kind } from "@lume/types";
|
||||||
import { Await, createFileRoute } from "@tanstack/react-router";
|
import { Await, createFileRoute } from "@tanstack/react-router";
|
||||||
import { Virtualizer } from "virtua";
|
import { Virtualizer } from "virtua";
|
||||||
import { defer } from "@tanstack/react-router";
|
import { defer } from "@tanstack/react-router";
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
export const Route = createFileRoute("/trending/notes")({
|
export const Route = createFileRoute("/trending/notes")({
|
||||||
loader: async ({ abortController }) => {
|
loader: async ({ abortController }) => {
|
||||||
@ -50,7 +50,7 @@ export function Screen() {
|
|||||||
className="inline-flex items-center gap-2 text-sm font-medium"
|
className="inline-flex items-center gap-2 text-sm font-medium"
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
<LoaderIcon className="animate-spin size-5" />
|
<Spinner className="size-5" />
|
||||||
Loading...
|
Loading...
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { LoaderIcon } from "@lume/icons";
|
import { Spinner, User } from "@lume/ui";
|
||||||
import { User } from "@lume/ui";
|
|
||||||
import { Await, defer } from "@tanstack/react-router";
|
import { Await, defer } from "@tanstack/react-router";
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
@ -34,7 +33,7 @@ export function Screen() {
|
|||||||
className="inline-flex items-center gap-2 text-sm font-medium"
|
className="inline-flex items-center gap-2 text-sm font-medium"
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
Loading...
|
Loading...
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { TextNote } from "@/components/text";
|
import { TextNote } from "@/components/text";
|
||||||
import { RepostNote } from "@/components/repost";
|
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 { Event, Kind } from "@lume/types";
|
||||||
import { FETCH_LIMIT } from "@lume/utils";
|
import { FETCH_LIMIT } from "@lume/utils";
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "@lume/ui";
|
||||||
|
|
||||||
export function EventList({ id }: { id: string }) {
|
export function EventList({ id }: { id: string }) {
|
||||||
const { ark } = useRouteContext({ strict: false });
|
const { ark } = useRouteContext({ strict: false });
|
||||||
@ -39,7 +40,7 @@ export function EventList({ id }: { id: string }) {
|
|||||||
<div>
|
<div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex h-20 w-full flex-col items-center justify-center gap-1">
|
<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>
|
</div>
|
||||||
) : !data.length ? (
|
) : !data.length ? (
|
||||||
<div className="flex items-center gap-2 rounded-xl bg-neutral-50 p-5 dark:bg-neutral-950">
|
<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"
|
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 ? (
|
{isFetchingNextPage ? (
|
||||||
<LoaderIcon className="size-5 animate-spin" />
|
<Spinner className="size-5" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ArrowRightCircleIcon className="size-5" />
|
<ArrowRightCircleIcon className="size-5" />
|
||||||
|
@ -33,7 +33,6 @@ export * from "./src/threads";
|
|||||||
export * from "./src/trash";
|
export * from "./src/trash";
|
||||||
export * from "./src/world";
|
export * from "./src/world";
|
||||||
export * from "./src/zap";
|
export * from "./src/zap";
|
||||||
export * from "./src/loader";
|
|
||||||
export * from "./src/trending";
|
export * from "./src/trending";
|
||||||
export * from "./src/empty";
|
export * from "./src/empty";
|
||||||
export * from "./src/cmd";
|
export * from "./src/cmd";
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import { SVGProps } from "react";
|
|
||||||
|
|
||||||
export function LoaderIcon(
|
|
||||||
props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<circle
|
|
||||||
className="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="4"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
className="opacity-75"
|
|
||||||
fill="currentColor"
|
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
@ -5,3 +5,4 @@ export * from "./column";
|
|||||||
// UI
|
// UI
|
||||||
export * from "./container";
|
export * from "./container";
|
||||||
export * from "./box";
|
export * from "./box";
|
||||||
|
export * from "./spinner";
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { ArrowDownIcon, LoaderIcon } from "@lume/icons";
|
import { ArrowDownIcon } from "@lume/icons";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "../../spinner";
|
||||||
|
|
||||||
export function NoteDownvote() {
|
export function NoteDownvote() {
|
||||||
const ark = useRouteContext({ strict: false });
|
const ark = useRouteContext({ strict: false });
|
||||||
@ -41,7 +42,7 @@ export function NoteDownvote() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoaderIcon className="size-4 animate-spin" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<ArrowDownIcon className="size-4" />
|
<ArrowDownIcon className="size-4" />
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LoaderIcon, QuoteIcon, RepostIcon } from "@lume/icons";
|
import { QuoteIcon, RepostIcon } from "@lume/icons";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||||
@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "../../spinner";
|
||||||
|
|
||||||
export function NoteRepost() {
|
export function NoteRepost() {
|
||||||
const { ark } = useRouteContext({ strict: false });
|
const { ark } = useRouteContext({ strict: false });
|
||||||
@ -46,7 +47,7 @@ export function NoteRepost() {
|
|||||||
className="group inline-flex size-7 items-center justify-center text-neutral-800 dark:text-neutral-200"
|
className="group inline-flex size-7 items-center justify-center text-neutral-800 dark:text-neutral-200"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoaderIcon className="size-4 animate-spin" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<RepostIcon
|
<RepostIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { ArrowUpIcon, LoaderIcon } from "@lume/icons";
|
import { ArrowUpIcon } from "@lume/icons";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNoteContext } from "../provider";
|
import { useNoteContext } from "../provider";
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import * as Tooltip from "@radix-ui/react-tooltip";
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "../../spinner";
|
||||||
|
|
||||||
export function NoteUpvote() {
|
export function NoteUpvote() {
|
||||||
const { ark } = useRouteContext({ strict: false });
|
const { ark } = useRouteContext({ strict: false });
|
||||||
@ -41,7 +42,7 @@ export function NoteUpvote() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoaderIcon className="size-4 animate-spin" />
|
<Spinner className="size-4" />
|
||||||
) : (
|
) : (
|
||||||
<ArrowUpIcon className="size-4" />
|
<ArrowUpIcon className="size-4" />
|
||||||
)}
|
)}
|
||||||
|
47
packages/ui/src/spinner.tsx
Normal file
47
packages/ui/src/spinner.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { cn } from "@lume/utils";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export function Spinner({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children?: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
const spinner = (
|
||||||
|
<span className={cn("block relative opacity-65 size-4", className)}>
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
<span className="spinner-leaf" />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (children === undefined) return spinner;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex items-center justify-center">
|
||||||
|
<span>
|
||||||
|
{/**
|
||||||
|
* `display: contents` removes the content from the accessibility tree in some browsers,
|
||||||
|
* so we force remove it with `aria-hidden`
|
||||||
|
*/}
|
||||||
|
<span
|
||||||
|
aria-hidden
|
||||||
|
style={{ display: "contents", visibility: "hidden" }}
|
||||||
|
// Workaround to use `inert` until https://github.com/facebook/react/pull/24730 is merged.
|
||||||
|
{...{ inert: true ? "" : undefined }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
<div className="absolute flex items-center justify-center">
|
||||||
|
<span>{spinner}</span>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { LoaderIcon } from "@lume/icons";
|
|
||||||
import { cn } from "@lume/utils";
|
import { cn } from "@lume/utils";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useUserContext } from "./provider";
|
import { useUserContext } from "./provider";
|
||||||
import { useRouteContext } from "@tanstack/react-router";
|
import { useRouteContext } from "@tanstack/react-router";
|
||||||
|
import { Spinner } from "../spinner";
|
||||||
|
|
||||||
export function UserFollowButton({ className }: { className?: string }) {
|
export function UserFollowButton({ className }: { className?: string }) {
|
||||||
const { ark } = useRouteContext({ strict: false });
|
const { ark } = useRouteContext({ strict: false });
|
||||||
@ -47,7 +47,7 @@ export function UserFollowButton({ className }: { className?: string }) {
|
|||||||
className={cn("w-max", className)}
|
className={cn("w-max", className)}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoaderIcon className="size-4 animate-spin" />
|
<Spinner className="size-4" />
|
||||||
) : followed ? (
|
) : followed ? (
|
||||||
t("user.unfollow")
|
t("user.unfollow")
|
||||||
) : (
|
) : (
|
||||||
|
Loading…
Reference in New Issue
Block a user