continued UI consolidation
This commit is contained in:
@@ -1,13 +1,6 @@
|
|||||||
import { Typewriter } from "./Typewriter";
|
import { Typewriter } from "./Typewriter";
|
||||||
import { useBars } from "~/context/bars";
|
import { useBars } from "~/context/bars";
|
||||||
import {
|
import { onMount, createSignal, Show, For, onCleanup } from "solid-js";
|
||||||
onMount,
|
|
||||||
createSignal,
|
|
||||||
Show,
|
|
||||||
For,
|
|
||||||
onCleanup,
|
|
||||||
createEffect
|
|
||||||
} from "solid-js";
|
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
import { insertSoftHyphens } from "~/lib/client-utils";
|
import { insertSoftHyphens } from "~/lib/client-utils";
|
||||||
import GitHub from "./icons/GitHub";
|
import GitHub from "./icons/GitHub";
|
||||||
@@ -102,7 +95,7 @@ export function RightBarContent() {
|
|||||||
const handleLinkClick = () => {
|
const handleLinkClick = () => {
|
||||||
if (
|
if (
|
||||||
typeof window !== "undefined" &&
|
typeof window !== "undefined" &&
|
||||||
window.innerWidth < BREAKPOINTS.MOBILE
|
window.innerWidth < BREAKPOINTS.MOBILE_MAX_WIDTH
|
||||||
) {
|
) {
|
||||||
setLeftBarVisible(false);
|
setLeftBarVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createSignal, createEffect, For, Show } from "solid-js";
|
import { createSignal, createEffect, For, Show } from "solid-js";
|
||||||
import Dropzone from "./Dropzone";
|
import Dropzone from "./Dropzone";
|
||||||
import XCircle from "~/components/icons/XCircle";
|
import AttachmentThumbnail from "~/components/ui/AttachmentThumbnail";
|
||||||
import AddImageToS3 from "~/lib/s3upload";
|
import AddImageToS3 from "~/lib/s3upload";
|
||||||
import { env } from "~/env/client";
|
import { env } from "~/env/client";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
@@ -147,42 +147,13 @@ export default function AddAttachmentSection(props: AddAttachmentSectionProps) {
|
|||||||
<div class="-mx-24 grid grid-cols-6 gap-4">
|
<div class="-mx-24 grid grid-cols-6 gap-4">
|
||||||
<For each={s3Files()}>
|
<For each={s3Files()}>
|
||||||
{(file) => (
|
{(file) => (
|
||||||
<div>
|
<AttachmentThumbnail
|
||||||
<button
|
fileUrl={getFileUrl(file.key)}
|
||||||
type="button"
|
isVideo={isVideoFile(file.key)}
|
||||||
class="hover:bg-crust hover:bg-opacity-80 absolute z-10 ml-4 pb-[120px]"
|
onCopy={() => copyToClipboard(file.key)}
|
||||||
onClick={() => removeImage(file.key)}
|
onRemove={() => removeImage(file.key)}
|
||||||
>
|
|
||||||
<XCircle
|
|
||||||
height={24}
|
|
||||||
width={24}
|
|
||||||
stroke={"currentColor"}
|
|
||||||
strokeWidth={1}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => copyToClipboard(file.key)}
|
|
||||||
class="relative"
|
|
||||||
>
|
|
||||||
<Show
|
|
||||||
when={isVideoFile(file.key)}
|
|
||||||
fallback={
|
|
||||||
<img
|
|
||||||
src={getFileUrl(file.key)}
|
|
||||||
class="mx-4 my-auto h-36 w-36 object-cover"
|
|
||||||
alt="attachment"
|
alt="attachment"
|
||||||
/>
|
/>
|
||||||
}
|
|
||||||
>
|
|
||||||
<video
|
|
||||||
src={getFileUrl(file.key)}
|
|
||||||
class="mx-4 my-auto h-36 w-36 object-cover"
|
|
||||||
controls
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
<Show when={newFileHolder().length > 0}>
|
<Show when={newFileHolder().length > 0}>
|
||||||
@@ -190,45 +161,17 @@ export default function AddAttachmentSection(props: AddAttachmentSectionProps) {
|
|||||||
</Show>
|
</Show>
|
||||||
<For each={newFileHolder()}>
|
<For each={newFileHolder()}>
|
||||||
{(file, index) => (
|
{(file, index) => (
|
||||||
<div>
|
<AttachmentThumbnail
|
||||||
<button
|
fileUrl={file}
|
||||||
type="button"
|
isVideo={fileTypes()[index()]?.startsWith("video/") || false}
|
||||||
class="hover:bg-crust hover:bg-opacity-80 absolute z-10 ml-4 pb-[120px]"
|
onCopy={() =>
|
||||||
onClick={() =>
|
|
||||||
removeNewImage(index(), newFileHolderKeys()[index()])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<XCircle
|
|
||||||
height={24}
|
|
||||||
width={24}
|
|
||||||
stroke={"currentColor"}
|
|
||||||
strokeWidth={1}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() =>
|
|
||||||
copyToClipboard(newFileHolderKeys()[index()] as string)
|
copyToClipboard(newFileHolderKeys()[index()] as string)
|
||||||
}
|
}
|
||||||
>
|
onRemove={() =>
|
||||||
<Show
|
removeNewImage(index(), newFileHolderKeys()[index()])
|
||||||
when={fileTypes()[index()]?.startsWith("video/")}
|
}
|
||||||
fallback={
|
|
||||||
<img
|
|
||||||
src={file}
|
|
||||||
class="mx-4 my-auto h-36 w-36 object-cover"
|
|
||||||
alt="new attachment"
|
alt="new attachment"
|
||||||
/>
|
/>
|
||||||
}
|
|
||||||
>
|
|
||||||
<video
|
|
||||||
src={file}
|
|
||||||
class="mx-4 my-auto h-36 w-36 object-cover"
|
|
||||||
controls
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import ReplyIcon from "~/components/icons/ReplyIcon";
|
|||||||
import TrashIcon from "~/components/icons/TrashIcon";
|
import TrashIcon from "~/components/icons/TrashIcon";
|
||||||
import EditIcon from "~/components/icons/EditIcon";
|
import EditIcon from "~/components/icons/EditIcon";
|
||||||
import ThumbsUpEmoji from "~/components/icons/emojis/ThumbsUp";
|
import ThumbsUpEmoji from "~/components/icons/emojis/ThumbsUp";
|
||||||
import LoadingSpinner from "~/components/LoadingSpinner";
|
import Button from "~/components/ui/Button";
|
||||||
|
import IconButton from "~/components/ui/IconButton";
|
||||||
import CommentInputBlock from "./CommentInputBlock";
|
import CommentInputBlock from "./CommentInputBlock";
|
||||||
import ReactionBar from "./ReactionBar";
|
import ReactionBar from "./ReactionBar";
|
||||||
|
|
||||||
@@ -254,32 +255,46 @@ export default function CommentBlock(props: CommentBlockProps) {
|
|||||||
|
|
||||||
{/* Delete button */}
|
{/* Delete button */}
|
||||||
<Show when={canDelete()}>
|
<Show when={canDelete()}>
|
||||||
<button class="z-100" onClick={deleteCommentTrigger}>
|
<IconButton
|
||||||
<Show
|
icon={
|
||||||
when={!deletionLoading()}
|
|
||||||
fallback={<LoadingSpinner height={24} width={24} />}
|
|
||||||
>
|
|
||||||
<TrashIcon
|
<TrashIcon
|
||||||
height={24}
|
height={24}
|
||||||
width={24}
|
width={24}
|
||||||
stroke="var(--color-red)"
|
stroke="var(--color-red)"
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
/>
|
/>
|
||||||
</Show>
|
}
|
||||||
</button>
|
variant="danger"
|
||||||
|
loading={deletionLoading()}
|
||||||
|
onClick={deleteCommentTrigger}
|
||||||
|
aria-label="Delete comment"
|
||||||
|
class="z-100"
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Edit and Reply buttons */}
|
{/* Edit and Reply buttons */}
|
||||||
<div class="absolute flex">
|
<div class="absolute flex">
|
||||||
<Show when={canEdit()}>
|
<Show when={canEdit()}>
|
||||||
<button onClick={editCommentTrigger} class="px-2">
|
<IconButton
|
||||||
<EditIcon strokeWidth={1} height={24} width={24} />
|
icon={<EditIcon strokeWidth={1} height={24} width={24} />}
|
||||||
</button>
|
onClick={editCommentTrigger}
|
||||||
|
aria-label="Edit comment"
|
||||||
|
class="px-2"
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<button onClick={toggleCommentReplyBox} class="z-30">
|
<IconButton
|
||||||
<ReplyIcon color={replyIconColor()} height={24} width={24} />
|
icon={
|
||||||
</button>
|
<ReplyIcon
|
||||||
|
color={replyIconColor()}
|
||||||
|
height={24}
|
||||||
|
width={24}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onClick={toggleCommentReplyBox}
|
||||||
|
aria-label="Reply to comment"
|
||||||
|
class="z-30"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Reaction bar */}
|
{/* Reaction bar */}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createEffect } from "solid-js";
|
import { createEffect } from "solid-js";
|
||||||
import type { CommentInputBlockProps } from "~/types/comment";
|
import type { CommentInputBlockProps } from "~/types/comment";
|
||||||
|
import Button from "~/components/ui/Button";
|
||||||
|
|
||||||
export default function CommentInputBlock(props: CommentInputBlockProps) {
|
export default function CommentInputBlock(props: CommentInputBlockProps) {
|
||||||
let bodyRef: HTMLTextAreaElement | undefined;
|
let bodyRef: HTMLTextAreaElement | undefined;
|
||||||
@@ -37,17 +38,13 @@ export default function CommentInputBlock(props: CommentInputBlockProps) {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end pt-2">
|
<div class="flex justify-end pt-2">
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={props.commentSubmitLoading}
|
loading={props.commentSubmitLoading}
|
||||||
class={`${
|
variant="primary"
|
||||||
props.commentSubmitLoading
|
|
||||||
? "bg-surface2 opacity-50"
|
|
||||||
: "border-sapphire bg-blue hover:brightness-125"
|
|
||||||
} rounded border px-4 py-2 text-base font-light shadow-md transition-all duration-300 ease-in-out active:scale-90`}
|
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createSignal, Show } from "solid-js";
|
import { createSignal } from "solid-js";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
import TrashIcon from "~/components/icons/TrashIcon";
|
import TrashIcon from "~/components/icons/TrashIcon";
|
||||||
import LoadingSpinner from "~/components/LoadingSpinner";
|
import Button from "~/components/ui/Button";
|
||||||
|
|
||||||
export interface DeletePostButtonProps {
|
export interface DeletePostButtonProps {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -28,14 +28,14 @@ export default function DeletePostButton(props: DeletePostButtonProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={deletePostTrigger} class="flex w-full justify-end">
|
<form onSubmit={deletePostTrigger} class="flex w-full justify-end">
|
||||||
<button type="submit" class="hover:cursor-pointer">
|
<Button
|
||||||
<Show
|
type="submit"
|
||||||
when={!loading()}
|
variant="ghost"
|
||||||
fallback={<LoadingSpinner height={24} width={24} />}
|
loading={loading()}
|
||||||
|
class="hover:cursor-pointer"
|
||||||
>
|
>
|
||||||
<TrashIcon height={24} width={24} strokeWidth={1.5} />
|
<TrashIcon height={24} width={24} strokeWidth={1.5} />
|
||||||
</Show>
|
</Button>
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createSignal, Show } from "solid-js";
|
import { createSignal, Show } from "solid-js";
|
||||||
import type { EditCommentModalProps } from "~/types/comment";
|
import type { EditCommentModalProps } from "~/types/comment";
|
||||||
import Xmark from "~/components/icons/Xmark";
|
import Xmark from "~/components/icons/Xmark";
|
||||||
|
import Button from "~/components/ui/Button";
|
||||||
|
|
||||||
export default function EditCommentModal(props: EditCommentModalProps) {
|
export default function EditCommentModal(props: EditCommentModalProps) {
|
||||||
let bodyRef: HTMLTextAreaElement | undefined;
|
let bodyRef: HTMLTextAreaElement | undefined;
|
||||||
@@ -52,17 +53,13 @@ export default function EditCommentModal(props: EditCommentModalProps) {
|
|||||||
<label class="underlinedInputLabel">Edit Comment</label>
|
<label class="underlinedInputLabel">Edit Comment</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end pt-2">
|
<div class="flex justify-end pt-2">
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={props.editCommentLoading}
|
loading={props.editCommentLoading}
|
||||||
class={`${
|
variant="primary"
|
||||||
props.editCommentLoading
|
|
||||||
? "bg-surface2 opacity-50"
|
|
||||||
: "border-sapphire bg-blue hover:brightness-125"
|
|
||||||
} rounded border px-4 py-2 text-base shadow-md transition-all duration-300 ease-in-out active:scale-90`}
|
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<Show when={showNoChange()}>
|
<Show when={showNoChange()}>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import AddAttachmentSection from "~/components/blog/AddAttachmentSection";
|
|||||||
import XCircle from "~/components/icons/XCircle";
|
import XCircle from "~/components/icons/XCircle";
|
||||||
import AddImageToS3 from "~/lib/s3upload";
|
import AddImageToS3 from "~/lib/s3upload";
|
||||||
import Input from "~/components/ui/Input";
|
import Input from "~/components/ui/Input";
|
||||||
|
import Button from "~/components/ui/Button";
|
||||||
|
|
||||||
interface PostFormProps {
|
interface PostFormProps {
|
||||||
mode: "create" | "edit";
|
mode: "create" | "edit";
|
||||||
@@ -542,23 +543,14 @@ export default function PostForm(props: PostFormProps) {
|
|||||||
|
|
||||||
{/* Submit button */}
|
{/* Submit button */}
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading()}
|
loading={loading()}
|
||||||
class={`${
|
variant={published() ? "primary" : "secondary"}
|
||||||
loading()
|
class="text-crust w-36"
|
||||||
? "bg-surface2 cursor-not-allowed"
|
|
||||||
: published()
|
|
||||||
? "bg-peach hover:brightness-125"
|
|
||||||
: "bg-green hover:brightness-125"
|
|
||||||
} text-crust flex w-36 justify-center rounded py-3 transition-all duration-300 ease-out active:scale-90`}
|
|
||||||
>
|
>
|
||||||
{loading()
|
{published() ? "Publish!" : "Save as Draft"}
|
||||||
? "Loading..."
|
</Button>
|
||||||
: published()
|
|
||||||
? "Publish!"
|
|
||||||
: "Save as Draft"}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { For } from "solid-js";
|
import { For } from "solid-js";
|
||||||
import InfoIcon from "~/components/icons/InfoIcon";
|
import InfoIcon from "~/components/icons/InfoIcon";
|
||||||
import Xmark from "~/components/icons/Xmark";
|
import Xmark from "~/components/icons/Xmark";
|
||||||
|
import IconButton from "~/components/ui/IconButton";
|
||||||
|
|
||||||
export interface TagMakerProps {
|
export interface TagMakerProps {
|
||||||
tagInputValue: string;
|
tagInputValue: string;
|
||||||
@@ -42,13 +43,20 @@ export default function TagMaker(props: TagMakerProps) {
|
|||||||
<div class="overflow-hidden text-base overflow-ellipsis whitespace-nowrap">
|
<div class="overflow-hidden text-base overflow-ellipsis whitespace-nowrap">
|
||||||
{tag}
|
{tag}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<IconButton
|
||||||
type="button"
|
icon={
|
||||||
class="bg-mantle bg-opacity-50 absolute inset-0 flex items-center justify-center rounded-xl opacity-0 group-hover:opacity-100"
|
<Xmark
|
||||||
|
strokeWidth={1}
|
||||||
|
color={"white"}
|
||||||
|
height={24}
|
||||||
|
width={24}
|
||||||
|
/>
|
||||||
|
}
|
||||||
onClick={() => props.deleteTag(idx())}
|
onClick={() => props.deleteTag(idx())}
|
||||||
>
|
aria-label={`Remove tag ${tag}`}
|
||||||
<Xmark strokeWidth={1} color={"white"} height={24} width={24} />
|
variant="danger"
|
||||||
</button>
|
class="bg-mantle bg-opacity-50 absolute inset-0 flex items-center justify-center rounded-xl opacity-0 group-hover:opacity-100"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|||||||
60
src/components/ui/AttachmentThumbnail.tsx
Normal file
60
src/components/ui/AttachmentThumbnail.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Show } from "solid-js";
|
||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import IconButton from "./IconButton";
|
||||||
|
import XCircle from "~/components/icons/XCircle";
|
||||||
|
|
||||||
|
export interface AttachmentThumbnailProps {
|
||||||
|
/** The URL of the file (either S3 URL or data URL) */
|
||||||
|
fileUrl: string;
|
||||||
|
/** Whether the file is a video */
|
||||||
|
isVideo: boolean;
|
||||||
|
/** Callback when the copy button is clicked */
|
||||||
|
onCopy: () => void;
|
||||||
|
/** Callback when the remove button is clicked */
|
||||||
|
onRemove: () => void;
|
||||||
|
/** Alt text for the image */
|
||||||
|
alt?: string;
|
||||||
|
/** Additional CSS classes */
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AttachmentThumbnail(props: AttachmentThumbnailProps) {
|
||||||
|
return (
|
||||||
|
<div class={props.class}>
|
||||||
|
<IconButton
|
||||||
|
icon={
|
||||||
|
<XCircle
|
||||||
|
height={24}
|
||||||
|
width={24}
|
||||||
|
stroke={"currentColor"}
|
||||||
|
strokeWidth={1}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onClick={props.onRemove}
|
||||||
|
aria-label={`Remove ${props.alt || "attachment"}`}
|
||||||
|
variant="danger"
|
||||||
|
class="hover:bg-crust hover:bg-opacity-80 absolute z-10 ml-4 pb-[120px]"
|
||||||
|
/>
|
||||||
|
<button type="button" onClick={props.onCopy} class="relative">
|
||||||
|
<Show
|
||||||
|
when={props.isVideo}
|
||||||
|
fallback={
|
||||||
|
<img
|
||||||
|
src={props.fileUrl}
|
||||||
|
class="mx-4 my-auto h-36 w-36 object-cover"
|
||||||
|
alt={props.alt || "attachment"}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
src={props.fileUrl}
|
||||||
|
class="mx-4 my-auto h-36 w-36 object-cover"
|
||||||
|
controls
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AttachmentThumbnail };
|
||||||
83
src/components/ui/IconButton.tsx
Normal file
83
src/components/ui/IconButton.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { JSX, splitProps, Show } from "solid-js";
|
||||||
|
import { Spinner } from "~/components/Spinner";
|
||||||
|
|
||||||
|
export interface IconButtonProps extends Omit<
|
||||||
|
JSX.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
"children"
|
||||||
|
> {
|
||||||
|
icon: JSX.Element;
|
||||||
|
"aria-label": string;
|
||||||
|
variant?: "ghost" | "danger" | "primary";
|
||||||
|
size?: "sm" | "md" | "lg";
|
||||||
|
loading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function IconButton(props: IconButtonProps) {
|
||||||
|
const [local, others] = splitProps(props, [
|
||||||
|
"icon",
|
||||||
|
"aria-label",
|
||||||
|
"variant",
|
||||||
|
"size",
|
||||||
|
"loading",
|
||||||
|
"disabled",
|
||||||
|
"class"
|
||||||
|
]);
|
||||||
|
|
||||||
|
const variant = () => local.variant || "ghost";
|
||||||
|
const size = () => local.size || "md";
|
||||||
|
|
||||||
|
const baseClasses =
|
||||||
|
"inline-flex items-center justify-center rounded transition-all duration-200 ease-out focus:outline-none focus-visible:ring-2 focus-visible:ring-blue focus-visible:ring-offset-2";
|
||||||
|
|
||||||
|
const variantClasses = () => {
|
||||||
|
const isDisabledOrLoading = local.disabled || local.loading;
|
||||||
|
|
||||||
|
switch (variant()) {
|
||||||
|
case "ghost":
|
||||||
|
return isDisabledOrLoading
|
||||||
|
? "cursor-not-allowed opacity-50"
|
||||||
|
: "text-text hover:bg-surface0/50 active:scale-95";
|
||||||
|
case "danger":
|
||||||
|
return isDisabledOrLoading
|
||||||
|
? "cursor-not-allowed opacity-50"
|
||||||
|
: "text-red hover:bg-red/10 active:scale-95";
|
||||||
|
case "primary":
|
||||||
|
return isDisabledOrLoading
|
||||||
|
? "cursor-not-allowed opacity-50"
|
||||||
|
: "text-blue hover:bg-blue/10 active:scale-95";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sizeClasses = () => {
|
||||||
|
switch (size()) {
|
||||||
|
case "sm":
|
||||||
|
return "p-1";
|
||||||
|
case "md":
|
||||||
|
return "p-2";
|
||||||
|
case "lg":
|
||||||
|
return "p-3";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...others}
|
||||||
|
type="button"
|
||||||
|
disabled={local.disabled || local.loading}
|
||||||
|
aria-label={local["aria-label"]}
|
||||||
|
aria-busy={local.loading}
|
||||||
|
aria-disabled={local.disabled}
|
||||||
|
class={`${baseClasses} ${variantClasses()} ${sizeClasses()} ${local.class || ""}`}
|
||||||
|
>
|
||||||
|
<Show when={local.loading} fallback={local.icon}>
|
||||||
|
<Spinner size={20} />
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { IconButton };
|
||||||
Reference in New Issue
Block a user