@k8o/arte-odyssey 6.0.1 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/buttons/button/button.d.mts +1 -0
- package/dist/components/buttons/button/button.mjs +31 -11
- package/dist/components/buttons/icon-button/icon-button.d.mts +1 -0
- package/dist/components/buttons/icon-button/icon-button.mjs +19 -2
- package/dist/components/data-display/accordion/accordion-item.mjs +1 -1
- package/dist/components/data-display/accordion/context.mjs +1 -1
- package/dist/components/data-display/avatar/avatar.mjs +1 -1
- package/dist/components/data-display/baseline-status/baseline-status.mjs +13 -15
- package/dist/components/data-display/code/code.mjs +2 -2
- package/dist/components/feedback/toast/provider.mjs +10 -7
- package/dist/components/feedback/toast/toast.mjs +1 -1
- package/dist/components/form/autocomplete/autocomplete.mjs +14 -4
- package/dist/components/form/checkbox/checkbox.mjs +4 -2
- package/dist/components/form/checkbox-card/checkbox-card.mjs +1 -1
- package/dist/components/form/checkbox-group/checkbox-group.mjs +1 -1
- package/dist/components/form/file-field/file-field.d.mts +1 -1
- package/dist/components/form/file-field/file-field.mjs +7 -4
- package/dist/components/form/form/form.d.mts +10 -0
- package/dist/components/form/form/form.mjs +14 -0
- package/dist/components/form/form/index.d.mts +2 -0
- package/dist/components/form/form/index.mjs +2 -0
- package/dist/components/form/form-control/form-control.mjs +1 -1
- package/dist/components/form/number-field/number-field.mjs +7 -4
- package/dist/components/form/password-input/password-input.mjs +6 -3
- package/dist/components/form/radio/radio.mjs +8 -5
- package/dist/components/form/radio-card/radio-card.mjs +1 -1
- package/dist/components/form/select/select.mjs +4 -1
- package/dist/components/form/slider/slider.mjs +5 -2
- package/dist/components/form/switch/switch.mjs +8 -5
- package/dist/components/form/text-field/text-field.mjs +5 -1
- package/dist/components/form/textarea/textarea.mjs +14 -7
- package/dist/components/index.d.mts +2 -1
- package/dist/components/index.mjs +3 -2
- package/dist/components/navigation/breadcrumb/breadcrumb.d.mts +1 -1
- package/dist/components/navigation/pagination/pagination.mjs +2 -4
- package/dist/components/navigation/tabs/tabs.mjs +1 -1
- package/dist/components/overlays/dialog/dialog.mjs +1 -1
- package/dist/components/overlays/drawer/drawer.mjs +1 -1
- package/dist/components/overlays/dropdown-menu/dropdown-menu.mjs +1 -1
- package/dist/components/overlays/list-box/list-box.mjs +1 -1
- package/dist/components/overlays/modal/modal.mjs +20 -11
- package/dist/components/overlays/popover/popover.mjs +1 -1
- package/dist/components/providers/portal-root.mjs +1 -1
- package/dist/hooks/debounced-transition/index.d.mts +5 -0
- package/dist/hooks/debounced-transition/index.mjs +50 -0
- package/dist/hooks/deferred-debounce/index.d.mts +4 -0
- package/dist/hooks/deferred-debounce/index.mjs +9 -0
- package/dist/hooks/index.d.mts +3 -3
- package/dist/hooks/index.mjs +3 -3
- package/dist/hooks/interval/index.mjs +9 -6
- package/dist/hooks/resize/index.d.mts +0 -1
- package/dist/hooks/resize/index.mjs +4 -12
- package/dist/hooks/timeout/index.mjs +9 -6
- package/dist/hooks/window-resize/index.d.mts +0 -1
- package/dist/hooks/window-resize/index.mjs +5 -17
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +5 -4
- package/dist/styles/index.css +9 -14
- package/dist/styles/tokens.d.mts +84 -0
- package/dist/styles/tokens.mjs +752 -0
- package/docs/references/hooks.md +29 -27
- package/package.json +27 -20
- package/dist/hooks/debounce/index.d.mts +0 -6
- package/dist/hooks/debounce/index.mjs +0 -35
- package/dist/hooks/throttle/index.d.mts +0 -6
- package/dist/hooks/throttle/index.mjs +0 -53
|
@@ -1,29 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { Spinner } from "../../feedback/spinner/spinner.mjs";
|
|
4
|
+
import { useTransition } from "react";
|
|
5
|
+
import { useFormStatus } from "react-dom";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
7
|
//#region src/components/buttons/button/button.tsx
|
|
4
|
-
const Button = ({ ref, children, type = "button", size = "md", color = "primary", variant = "contained", disabled = false, fullWidth = false, onClick, startIcon, endIcon, ...rest }) => {
|
|
8
|
+
const Button = ({ ref, children, type = "button", size = "md", color = "primary", variant = "contained", disabled = false, fullWidth = false, onAction, onClick, startIcon, endIcon, ...rest }) => {
|
|
9
|
+
const [transitionPending, startTransition] = useTransition();
|
|
10
|
+
const { pending: formPending } = useFormStatus();
|
|
11
|
+
const isPending = transitionPending || type === "submit" && formPending;
|
|
12
|
+
const isDisabled = disabled || isPending;
|
|
13
|
+
const handleClick = onClick || onAction ? (event) => {
|
|
14
|
+
onClick?.(event);
|
|
15
|
+
if (event.defaultPrevented) return;
|
|
16
|
+
if (onAction) startTransition(async () => {
|
|
17
|
+
await onAction();
|
|
18
|
+
});
|
|
19
|
+
} : void 0;
|
|
20
|
+
const resolvedStartIcon = isPending ? /* @__PURE__ */ jsx(Spinner, {
|
|
21
|
+
label: "Loading",
|
|
22
|
+
size: size === "lg" ? "md" : "sm"
|
|
23
|
+
}) : startIcon;
|
|
5
24
|
return /* @__PURE__ */ jsxs("button", {
|
|
25
|
+
"aria-busy": isPending || void 0,
|
|
6
26
|
className: cn("cursor-pointer rounded-full border-2 text-center font-bold transition-colors", {
|
|
7
27
|
"border-transparent bg-primary-bg text-fg hover:bg-primary-bg-emphasize/80 active:bg-primary-bg-emphasize": variant === "contained" && color === "primary",
|
|
8
28
|
"border-transparent bg-secondary-bg text-fg hover:bg-secondary-bg-emphasize/80 active:bg-secondary-bg-emphasize": variant === "contained" && color === "secondary",
|
|
9
29
|
"border-transparent bg-bg-subtle text-fg-base hover:bg-bg-mute/80 active:bg-bg-mute": variant === "contained" && color === "gray",
|
|
10
|
-
"cursor-not-allowed opacity-35 hover:bg-primary-bg active:bg-primary-bg":
|
|
11
|
-
"cursor-not-allowed opacity-35 hover:bg-secondary-bg active:bg-secondary-bg":
|
|
12
|
-
"cursor-not-allowed opacity-35 hover:bg-bg-subtle active:bg-bg-subtle":
|
|
30
|
+
"cursor-not-allowed opacity-35 hover:bg-primary-bg active:bg-primary-bg": isDisabled && variant === "contained" && color === "primary",
|
|
31
|
+
"cursor-not-allowed opacity-35 hover:bg-secondary-bg active:bg-secondary-bg": isDisabled && variant === "contained" && color === "secondary",
|
|
32
|
+
"cursor-not-allowed opacity-35 hover:bg-bg-subtle active:bg-bg-subtle": isDisabled && variant === "contained" && color === "gray",
|
|
13
33
|
"border-primary-border bg-bg-base text-primary-fg hover:bg-bg-subtle active:bg-bg-mute": variant === "outlined" && color === "primary",
|
|
14
34
|
"border-secondary-border bg-bg-base text-secondary-fg hover:bg-bg-subtle active:bg-bg-mute": variant === "outlined" && color === "secondary",
|
|
15
35
|
"border-border-base bg-bg-base text-fg-base hover:bg-bg-subtle active:bg-bg-mute": variant === "outlined" && color === "gray",
|
|
16
|
-
"cursor-not-allowed bg-bg-base opacity-35 hover:bg-bg-base active:bg-bg-base":
|
|
36
|
+
"cursor-not-allowed bg-bg-base opacity-35 hover:bg-bg-base active:bg-bg-base": isDisabled && variant === "outlined",
|
|
17
37
|
"border-transparent bg-transparent text-fg-mute hover:bg-bg-subtle hover:text-fg-base active:bg-bg-mute active:text-fg-base": variant === "skeleton",
|
|
18
|
-
"cursor-not-allowed bg-transparent text-fg-mute opacity-35 hover:bg-transparent hover:text-fg-mute active:bg-transparent active:text-fg-mute":
|
|
19
|
-
}, "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", size === "sm" && "px-3 py-1 text-sm", size === "md" && "px-4 py-2 text-md", size === "lg" && "px-6 py-3 text-lg", fullWidth && "w-full", Boolean(
|
|
20
|
-
disabled,
|
|
21
|
-
onClick,
|
|
38
|
+
"cursor-not-allowed bg-transparent text-fg-mute opacity-35 hover:bg-transparent hover:text-fg-mute active:bg-transparent active:text-fg-mute": isDisabled && variant === "skeleton"
|
|
39
|
+
}, "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", size === "sm" && "px-3 py-1 text-sm", size === "md" && "px-4 py-2 text-md", size === "lg" && "px-6 py-3 text-lg", fullWidth && "w-full", Boolean(resolvedStartIcon ?? endIcon) && "flex items-center gap-2", resolvedStartIcon && endIcon ? "justify-between" : resolvedStartIcon && variant !== "skeleton" ? "justify-center" : endIcon && "justify-between"),
|
|
40
|
+
disabled: isDisabled,
|
|
41
|
+
onClick: handleClick,
|
|
22
42
|
ref,
|
|
23
43
|
type,
|
|
24
44
|
...rest,
|
|
25
45
|
children: [
|
|
26
|
-
|
|
46
|
+
resolvedStartIcon,
|
|
27
47
|
children,
|
|
28
48
|
endIcon
|
|
29
49
|
]
|
|
@@ -5,6 +5,7 @@ type Props = {
|
|
|
5
5
|
size?: 'sm' | 'md' | 'lg';
|
|
6
6
|
bg?: 'transparent' | 'base' | 'primary' | 'secondary';
|
|
7
7
|
label: string;
|
|
8
|
+
onAction?: () => void | Promise<void>;
|
|
8
9
|
} & Omit<HTMLProps<HTMLButtonElement>, 'size' | 'type'>;
|
|
9
10
|
declare const IconButton: FC<Props>;
|
|
10
11
|
//#endregion
|
|
@@ -1,10 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
+
import { useTransition } from "react";
|
|
4
|
+
import { useFormStatus } from "react-dom";
|
|
2
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
6
|
//#region src/components/buttons/icon-button/icon-button.tsx
|
|
4
|
-
const IconButton = ({ ref, size = "md", bg = "transparent", label, children, ...props }) => {
|
|
7
|
+
const IconButton = ({ ref, size = "md", bg = "transparent", label, children, onAction, onClick, disabled, ...props }) => {
|
|
8
|
+
const [transitionPending, startTransition] = useTransition();
|
|
9
|
+
const { pending: formPending } = useFormStatus();
|
|
10
|
+
const isPending = transitionPending || formPending;
|
|
11
|
+
const isDisabled = Boolean(disabled) || isPending;
|
|
12
|
+
const handleClick = onClick || onAction ? (event) => {
|
|
13
|
+
onClick?.(event);
|
|
14
|
+
if (event.defaultPrevented) return;
|
|
15
|
+
if (onAction) startTransition(async () => {
|
|
16
|
+
await onAction();
|
|
17
|
+
});
|
|
18
|
+
} : void 0;
|
|
5
19
|
return /* @__PURE__ */ jsxs("button", {
|
|
20
|
+
"aria-busy": isPending || void 0,
|
|
6
21
|
"aria-label": props.role ? label : void 0,
|
|
7
|
-
className: cn("inline-flex cursor-pointer rounded-full transition-colors", "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", (bg === "transparent" || bg === "base") && "hover:bg-bg-subtle active:bg-bg-mute", bg === "base" && "bg-bg-base", bg === "transparent" && "bg-transparent", bg === "primary" && "bg-primary-bg hover:bg-primary-bg-emphasize/80 active:bg-primary-bg-emphasize", bg === "secondary" && "bg-secondary-bg hover:bg-secondary-bg-emphasize/80 active:bg-secondary-bg-emphasize", size === "sm" && "p-1", size === "md" && "p-2", size === "lg" && "p-3",
|
|
22
|
+
className: cn("inline-flex cursor-pointer rounded-full transition-colors", "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", (bg === "transparent" || bg === "base") && "hover:bg-bg-subtle active:bg-bg-mute", bg === "base" && "bg-bg-base", bg === "transparent" && "bg-transparent", bg === "primary" && "bg-primary-bg hover:bg-primary-bg-emphasize/80 active:bg-primary-bg-emphasize", bg === "secondary" && "bg-secondary-bg hover:bg-secondary-bg-emphasize/80 active:bg-secondary-bg-emphasize", size === "sm" && "p-1", size === "md" && "p-2", size === "lg" && "p-3", isDisabled && "cursor-not-allowed opacity-50 hover:bg-transparent active:bg-transparent"),
|
|
23
|
+
disabled: isDisabled,
|
|
24
|
+
onClick: handleClick,
|
|
8
25
|
ref,
|
|
9
26
|
...props,
|
|
10
27
|
children: [!props.role && /* @__PURE__ */ jsx("span", {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { AccordionItemProvider } from "./context.mjs";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
3
|
import { useId } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/data-display/accordion/accordion-item.tsx
|
|
6
6
|
const AccordionItem = ({ children, defaultOpen = false }) => {
|
|
7
7
|
return /* @__PURE__ */ jsx(AccordionItemProvider, {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useDisclosure } from "../../../hooks/disclosure/index.mjs";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
3
|
import { createContext, use } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/data-display/accordion/context.tsx
|
|
6
6
|
const OpenContext = createContext(false);
|
|
7
7
|
const ToggleOpenContext = createContext(void 0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
3
|
import { useState } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/data-display/avatar/avatar.tsx
|
|
6
6
|
const getInitials = (name) => {
|
|
7
7
|
if (!name) return "?";
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { Suspense, use } from "react";
|
|
2
3
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useSyncExternalStore } from "react";
|
|
4
4
|
//#region src/components/data-display/baseline-status/baseline-status.tsx
|
|
5
|
-
let
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
return () => {};
|
|
15
|
-
}, () => didInit, () => false)) return /* @__PURE__ */ jsx("div", {
|
|
16
|
-
className: "max-w-full animate-pulse rounded-lg border border-border-base bg-bg-base p-4",
|
|
17
|
-
style: { height: "120px" }
|
|
18
|
-
});
|
|
5
|
+
let loadPromise = null;
|
|
6
|
+
const loadBaselineStatus = () => {
|
|
7
|
+
loadPromise ??= import("baseline-status");
|
|
8
|
+
return loadPromise;
|
|
9
|
+
};
|
|
10
|
+
const BaselineStatusResolved = ({ featureId }) => {
|
|
11
|
+
use(loadBaselineStatus());
|
|
19
12
|
return /* @__PURE__ */ jsx("baseline-status", {
|
|
20
13
|
className: "wrap-normal max-w-full rounded-lg border border-border-base bg-bg-base p-4",
|
|
21
14
|
featureId
|
|
22
15
|
});
|
|
23
16
|
};
|
|
17
|
+
const BaselineStatusSkeleton = () => /* @__PURE__ */ jsx("div", { className: "h-58 max-w-full animate-pulse rounded-lg border border-border-base bg-bg-base p-4 sm:h-40 md:h-30" });
|
|
18
|
+
const BaselineStatus = ({ featureId }) => /* @__PURE__ */ jsx(Suspense, {
|
|
19
|
+
fallback: /* @__PURE__ */ jsx(BaselineStatusSkeleton, {}),
|
|
20
|
+
children: /* @__PURE__ */ jsx(BaselineStatusResolved, { featureId })
|
|
21
|
+
});
|
|
24
22
|
//#endregion
|
|
25
23
|
export { BaselineStatus };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { findAllColors } from "../../../helpers/color/find-all-colors.mjs";
|
|
2
|
+
import { Fragment } from "react";
|
|
2
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Fragment as Fragment$1 } from "react";
|
|
4
4
|
//#region src/components/data-display/code/code.tsx
|
|
5
5
|
const Code = ({ children }) => {
|
|
6
6
|
const colors = findAllColors(children);
|
|
@@ -12,7 +12,7 @@ const Code = ({ children }) => {
|
|
|
12
12
|
let lastIndex = 0;
|
|
13
13
|
colors.forEach((colorInfo, index) => {
|
|
14
14
|
if (colorInfo.start > lastIndex) parts.push(children.slice(lastIndex, colorInfo.start));
|
|
15
|
-
parts.push(/* @__PURE__ */ jsxs(Fragment
|
|
15
|
+
parts.push(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
16
16
|
"aria-label": `Color: ${colorInfo.color}`,
|
|
17
17
|
className: "inline-block h-3 w-3 shrink-0 rounded-sm border border-border-base",
|
|
18
18
|
role: "img",
|
|
@@ -2,23 +2,26 @@
|
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
3
|
import { uuidV4 } from "../../../helpers/uuid-v4.mjs";
|
|
4
4
|
import { Toast } from "./toast.mjs";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import { createContext, use, useCallback, useEffect, useRef, useState } from "react";
|
|
6
|
+
import { createPortal } from "react-dom";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
import { AnimatePresence } from "motion/react";
|
|
8
9
|
import * as motion$1 from "motion/react-client";
|
|
9
|
-
import { createPortal } from "react-dom";
|
|
10
10
|
//#region src/components/feedback/toast/provider.tsx
|
|
11
|
+
const MAX_TOAST_COUNT = 5;
|
|
11
12
|
const SetToastContext = createContext(void 0);
|
|
12
13
|
const useToast = () => {
|
|
13
14
|
const setToasts = use(SetToastContext);
|
|
14
15
|
if (!setToasts) throw new Error("useToast must be used within a ToastProvider");
|
|
15
16
|
return {
|
|
16
17
|
onOpen: useCallback((status, message) => {
|
|
17
|
-
setToasts((prev) =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
setToasts((prev) => {
|
|
19
|
+
return [...prev, {
|
|
20
|
+
id: uuidV4(),
|
|
21
|
+
status,
|
|
22
|
+
message
|
|
23
|
+
}].slice(-MAX_TOAST_COUNT);
|
|
24
|
+
});
|
|
22
25
|
}, [setToasts]),
|
|
23
26
|
onClose: useCallback((id) => {
|
|
24
27
|
setToasts((prev) => prev.filter((toast) => toast.id !== id));
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { Alert } from "../alert/alert.mjs";
|
|
3
3
|
import { useTimeout } from "../../../hooks/timeout/index.mjs";
|
|
4
4
|
import { useToast } from "./provider.mjs";
|
|
5
|
-
import { jsx } from "react/jsx-runtime";
|
|
6
5
|
import { useCallback } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/feedback/toast/toast.tsx
|
|
8
8
|
const DELAY_MS = 5e3;
|
|
9
9
|
const Toast = ({ id, status, message }) => {
|
|
@@ -3,8 +3,10 @@ import { cn } from "../../../helpers/cn.mjs";
|
|
|
3
3
|
import { IconButton } from "../../buttons/icon-button/icon-button.mjs";
|
|
4
4
|
import { CloseIcon } from "../../icons/lucide.mjs";
|
|
5
5
|
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { useDeferredDebounce } from "../../../hooks/deferred-debounce/index.mjs";
|
|
7
7
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
8
|
+
import { useFormStatus } from "react-dom";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
10
|
//#region src/components/form/autocomplete/autocomplete.tsx
|
|
9
11
|
const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, options, value, defaultValue, onChange }) => {
|
|
10
12
|
const [currentValue, handleChange] = useControllableState({
|
|
@@ -16,7 +18,10 @@ const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequir
|
|
|
16
18
|
const [open, setOpen] = useState(false);
|
|
17
19
|
const [text, setText] = useState("");
|
|
18
20
|
const [selectIndex, setSelectIndex] = useState();
|
|
19
|
-
const
|
|
21
|
+
const [deferredText, isPending] = useDeferredDebounce(text);
|
|
22
|
+
const filteredOptions = options.filter((option) => option.label.includes(deferredText));
|
|
23
|
+
const { pending: formPending } = useFormStatus();
|
|
24
|
+
const isDisabledResolved = isDisabled || formPending;
|
|
20
25
|
const reset = useCallback(() => {
|
|
21
26
|
setText("");
|
|
22
27
|
setOpen(false);
|
|
@@ -70,7 +75,7 @@ const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequir
|
|
|
70
75
|
"aria-required": isRequired,
|
|
71
76
|
autoComplete: "off",
|
|
72
77
|
className: cn("grow bg-transparent focus-visible:outline-hidden", "disabled:cursor-not-allowed"),
|
|
73
|
-
disabled:
|
|
78
|
+
disabled: isDisabledResolved,
|
|
74
79
|
id,
|
|
75
80
|
onBlur: (e) => {
|
|
76
81
|
if (e.relatedTarget?.id.startsWith(`${id}_option_`)) return;
|
|
@@ -112,6 +117,10 @@ const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequir
|
|
|
112
117
|
return;
|
|
113
118
|
}
|
|
114
119
|
if (e.key === "Enter" && selectIndex !== void 0 && selectIndex >= 0) {
|
|
120
|
+
if (isPending) {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
115
124
|
const selected = filteredOptions[selectIndex];
|
|
116
125
|
if (!selected) return;
|
|
117
126
|
if (currentValue.includes(selected.value)) {
|
|
@@ -145,7 +154,8 @@ const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequir
|
|
|
145
154
|
className: "absolute top-1 z-10 w-full rounded-xl bg-bg-raised shadow-md",
|
|
146
155
|
role: "presentation",
|
|
147
156
|
children: /* @__PURE__ */ jsxs("ul", {
|
|
148
|
-
|
|
157
|
+
"aria-busy": isPending || void 0,
|
|
158
|
+
className: cn("max-h-96 py-2 transition-opacity", isPending && "opacity-60"),
|
|
149
159
|
id: `${id}_listbox`,
|
|
150
160
|
children: [filteredOptions.length === 0 && /* @__PURE__ */ jsx("li", {
|
|
151
161
|
className: "px-3 py-2 text-fg-mute",
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
3
|
import { CheckIcon } from "../../icons/lucide.mjs";
|
|
4
4
|
import { useCheckboxGroupContext } from "../checkbox-group/checkbox-group.mjs";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import { useState } from "react";
|
|
6
|
+
import { useFormStatus } from "react-dom";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/form/checkbox/checkbox.tsx
|
|
8
9
|
const Checkbox = ({ name, itemValue, isDisabled = false, label, value, defaultChecked, onChange }) => {
|
|
9
10
|
const groupContext = useCheckboxGroupContext();
|
|
11
|
+
const { pending } = useFormStatus();
|
|
10
12
|
const [internalChecked, setInternalChecked] = useState(defaultChecked ?? false);
|
|
11
13
|
const groupItemValue = itemValue ?? "";
|
|
12
14
|
if (groupContext && !itemValue) throw new Error("Checkbox inside CheckboxGroup requires itemValue");
|
|
13
15
|
const isControlled = value !== void 0;
|
|
14
|
-
const isDisabledResolved = isDisabled || groupContext?.isDisabled;
|
|
16
|
+
const isDisabledResolved = isDisabled || groupContext?.isDisabled || pending;
|
|
15
17
|
const checked = groupContext ? groupContext.currentValue.includes(groupItemValue) : isControlled ? value : internalChecked;
|
|
16
18
|
const setChecked = (nextChecked) => {
|
|
17
19
|
if (!isControlled) setInternalChecked(nextChecked);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
3
|
import { CheckIcon } from "../../icons/lucide.mjs";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
4
|
import { useId, useState } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/form/checkbox-card/checkbox-card.tsx
|
|
7
7
|
const CheckboxCard = ({ labelId, name, isDisabled, isInvalid = false, options, value, defaultValue, onChange }) => {
|
|
8
8
|
const groupId = useId();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
3
|
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
4
|
import { createContext, use } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/form/checkbox-group/checkbox-group.tsx
|
|
7
7
|
const CheckboxGroupContext = createContext(void 0);
|
|
8
8
|
const useCheckboxGroupContext = () => use(CheckboxGroupContext);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
1
|
import { ChangeEventHandler, FC, PropsWithChildren, ReactElement } from "react";
|
|
2
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/components/form/file-field/file-field.d.ts
|
|
5
5
|
declare const FileField: {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import { IconButton } from "../../buttons/icon-button/icon-button.mjs";
|
|
3
3
|
import { CloseIcon } from "../../icons/lucide.mjs";
|
|
4
4
|
import { uuidV4 } from "../../../helpers/uuid-v4.mjs";
|
|
5
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import { createContext, use, useCallback, useId, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { useFormStatus } from "react-dom";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/form/file-field/file-field.tsx
|
|
8
9
|
const FileFieldContext = createContext(null);
|
|
9
10
|
const FileFieldProvider = FileFieldContext;
|
|
@@ -15,6 +16,8 @@ const useFileFieldContext = () => {
|
|
|
15
16
|
const Root = ({ children, id, name, describedbyId, isDisabled = false, isInvalid = false, isRequired = false, accept, multiple = false, maxFiles, onChange, webkitDirectory = false }) => {
|
|
16
17
|
const generatedId = useId();
|
|
17
18
|
const inputRef = useRef(null);
|
|
19
|
+
const { pending } = useFormStatus();
|
|
20
|
+
const isDisabledResolved = isDisabled || pending;
|
|
18
21
|
const [acceptedFiles, setAcceptedFiles] = useState([]);
|
|
19
22
|
const onFilesChange = useCallback((event) => {
|
|
20
23
|
onChange?.(event);
|
|
@@ -50,13 +53,13 @@ const Root = ({ children, id, name, describedbyId, isDisabled = false, isInvalid
|
|
|
50
53
|
}, []);
|
|
51
54
|
return /* @__PURE__ */ jsx(FileFieldProvider, {
|
|
52
55
|
value: useMemo(() => ({
|
|
53
|
-
isDisabled,
|
|
56
|
+
isDisabled: isDisabledResolved,
|
|
54
57
|
isInvalid,
|
|
55
58
|
acceptedFiles,
|
|
56
59
|
onFileDelete,
|
|
57
60
|
openFilePicker
|
|
58
61
|
}), [
|
|
59
|
-
|
|
62
|
+
isDisabledResolved,
|
|
60
63
|
isInvalid,
|
|
61
64
|
acceptedFiles,
|
|
62
65
|
onFileDelete,
|
|
@@ -69,7 +72,7 @@ const Root = ({ children, id, name, describedbyId, isDisabled = false, isInvalid
|
|
|
69
72
|
"aria-describedby": describedbyId,
|
|
70
73
|
"aria-invalid": isInvalid,
|
|
71
74
|
className: "sr-only",
|
|
72
|
-
disabled:
|
|
75
|
+
disabled: isDisabledResolved,
|
|
73
76
|
id: id ?? generatedId,
|
|
74
77
|
multiple,
|
|
75
78
|
name,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC, FormHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/form/form/form.d.ts
|
|
4
|
+
type Props = {
|
|
5
|
+
action?: ((formData: FormData) => void | Promise<void>) | string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
} & Omit<FormHTMLAttributes<HTMLFormElement>, 'action' | 'children'>;
|
|
8
|
+
declare const Form: FC<Props>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Form };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/form/form/form.tsx
|
|
5
|
+
const Form = ({ action, className, children, ...rest }) => {
|
|
6
|
+
return /* @__PURE__ */ jsx("form", {
|
|
7
|
+
action,
|
|
8
|
+
className: cn("flex flex-col gap-6", className),
|
|
9
|
+
...rest,
|
|
10
|
+
children
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Form };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { useId } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/form/form-control/form-control.tsx
|
|
5
5
|
const FormControl = ({ isDisabled = false, isInvalid = false, isRequired = false, label, labelAs = "label", helpText, errorText, renderInput }) => {
|
|
6
6
|
const id = useId();
|
|
@@ -4,8 +4,9 @@ import { ChevronIcon } from "../../icons/lucide.mjs";
|
|
|
4
4
|
import { between } from "../../../helpers/number/between.mjs";
|
|
5
5
|
import { toPrecision } from "../../../helpers/number/to-precision.mjs";
|
|
6
6
|
import { cast } from "../../../helpers/number/cast.mjs";
|
|
7
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { useState } from "react";
|
|
8
|
+
import { useFormStatus } from "react-dom";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/components/form/number-field/number-field.tsx
|
|
10
11
|
const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, value, defaultValue, onChange, step = 1, precision = 0, max = 9007199254740991, min = -9007199254740991, placeholder }) => {
|
|
11
12
|
const isControlled = value !== void 0;
|
|
@@ -13,6 +14,7 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
13
14
|
const [internalValue, setInternalValue] = useState(initialValue);
|
|
14
15
|
const [displayValue, setDisplayValue] = useState(initialValue.toFixed(precision));
|
|
15
16
|
const [prevValue, setPrevValue] = useState(initialValue);
|
|
17
|
+
const { pending } = useFormStatus();
|
|
16
18
|
const currentValue = isControlled ? value : internalValue;
|
|
17
19
|
if (isControlled && value !== prevValue) {
|
|
18
20
|
setDisplayValue(value.toFixed(precision));
|
|
@@ -33,11 +35,12 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
33
35
|
"aria-valuenow": currentValue,
|
|
34
36
|
autoComplete: "off",
|
|
35
37
|
autoCorrect: "off",
|
|
36
|
-
className: cn("h-full w-full grow bg-transparent pr-8 pl-3 focus-visible:outline-hidden", "disabled:cursor-not-allowed"),
|
|
38
|
+
className: cn("h-full w-full grow bg-transparent pr-8 pl-3 focus-visible:outline-hidden", "disabled:cursor-not-allowed", "read-only:cursor-not-allowed"),
|
|
37
39
|
disabled: isDisabled,
|
|
38
40
|
id,
|
|
39
41
|
inputMode: "decimal",
|
|
40
42
|
name,
|
|
43
|
+
readOnly: pending || void 0,
|
|
41
44
|
onBlur: () => {
|
|
42
45
|
const newValue = between(cast(displayValue, precision), min, max);
|
|
43
46
|
handleChange(newValue);
|
|
@@ -69,7 +72,7 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
69
72
|
className: "absolute right-1 flex h-full flex-col py-1",
|
|
70
73
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
71
74
|
className: cn("flex w-6 grow items-center justify-center rounded-md text-fg-mute transition-colors", "hover:bg-bg-mute hover:text-fg-base", "disabled:cursor-not-allowed disabled:text-fg-mute disabled:hover:bg-transparent"),
|
|
72
|
-
disabled: isDisabled,
|
|
75
|
+
disabled: isDisabled || pending,
|
|
73
76
|
onClick: () => {
|
|
74
77
|
const newValue = between(toPrecision(cast(displayValue, precision) + step, precision), min, max);
|
|
75
78
|
handleChange(newValue);
|
|
@@ -86,7 +89,7 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
86
89
|
})]
|
|
87
90
|
}), /* @__PURE__ */ jsxs("button", {
|
|
88
91
|
className: cn("flex w-6 grow items-center justify-center rounded-md text-fg-mute transition-colors", "hover:bg-bg-mute hover:text-fg-base", "disabled:cursor-not-allowed disabled:text-fg-mute disabled:hover:bg-transparent"),
|
|
89
|
-
disabled: isDisabled,
|
|
92
|
+
disabled: isDisabled || pending,
|
|
90
93
|
onClick: () => {
|
|
91
94
|
const newValue = between(toPrecision(cast(displayValue, precision) - step, precision), min, max);
|
|
92
95
|
handleChange(newValue);
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
3
|
import { ViewIcon, ViewOffIcon } from "../../icons/lucide.mjs";
|
|
4
4
|
import { useDisclosure } from "../../../hooks/disclosure/index.mjs";
|
|
5
|
+
import { useFormStatus } from "react-dom";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/form/password-input/password-input.tsx
|
|
7
8
|
const PasswordInput = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, placeholder, autoComplete = "current-password", showLabel = "Show password", hideLabel = "Hide password", defaultValue, value, onChange }) => {
|
|
8
9
|
const { isOpen: isVisible, toggle: toggleVisible } = useDisclosure();
|
|
10
|
+
const { pending } = useFormStatus();
|
|
9
11
|
return /* @__PURE__ */ jsxs("div", {
|
|
10
12
|
className: "relative w-full",
|
|
11
13
|
children: [/* @__PURE__ */ jsx("input", {
|
|
@@ -13,20 +15,21 @@ const PasswordInput = ({ id, name, describedbyId, isInvalid, isDisabled, isRequi
|
|
|
13
15
|
"aria-invalid": isInvalid,
|
|
14
16
|
"aria-required": isRequired,
|
|
15
17
|
autoComplete,
|
|
16
|
-
className: cn("w-full rounded-xl border border-border-base bg-bg-base px-3 py-2 pr-12", "aria-invalid:border-border-error", "disabled:cursor-not-allowed disabled:border-border-mute disabled:bg-bg-mute disabled:hover:bg-bg-mute", "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info"),
|
|
18
|
+
className: cn("w-full rounded-xl border border-border-base bg-bg-base px-3 py-2 pr-12", "aria-invalid:border-border-error", "disabled:cursor-not-allowed disabled:border-border-mute disabled:bg-bg-mute disabled:hover:bg-bg-mute", "read-only:cursor-not-allowed read-only:bg-bg-subtle", "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info"),
|
|
17
19
|
defaultValue,
|
|
18
20
|
disabled: isDisabled,
|
|
19
21
|
id,
|
|
20
22
|
name,
|
|
21
23
|
onChange,
|
|
22
24
|
placeholder,
|
|
25
|
+
readOnly: pending || void 0,
|
|
23
26
|
required: isRequired,
|
|
24
27
|
type: isVisible ? "text" : "password",
|
|
25
28
|
value
|
|
26
29
|
}), /* @__PURE__ */ jsx("button", {
|
|
27
30
|
"aria-label": isVisible ? hideLabel : showLabel,
|
|
28
|
-
className: cn("absolute top-1/2 right-2 inline-flex -translate-y-1/2 items-center justify-center rounded-md p-1 text-fg-mute transition-colors", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", !isDisabled && "hover:bg-bg-mute hover:text-fg-base", isDisabled && "cursor-not-allowed text-fg-mute/70"),
|
|
29
|
-
disabled: isDisabled,
|
|
31
|
+
className: cn("absolute top-1/2 right-2 inline-flex -translate-y-1/2 items-center justify-center rounded-md p-1 text-fg-mute transition-colors", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", !isDisabled && !pending && "hover:bg-bg-mute hover:text-fg-base", (isDisabled || pending) && "cursor-not-allowed text-fg-mute/70"),
|
|
32
|
+
disabled: isDisabled || pending,
|
|
30
33
|
onClick: toggleVisible,
|
|
31
34
|
type: "button",
|
|
32
35
|
children: isVisible ? /* @__PURE__ */ jsx(ViewOffIcon, { size: "sm" }) : /* @__PURE__ */ jsx(ViewIcon, { size: "sm" })
|
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { useState } from "react";
|
|
4
|
+
import { useFormStatus } from "react-dom";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
6
|
//#region src/components/form/radio/radio.tsx
|
|
6
7
|
const Radio = ({ labelId, name, isDisabled, value, defaultValue, onChange, options }) => {
|
|
7
8
|
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
9
|
+
const { pending } = useFormStatus();
|
|
8
10
|
const isControlled = value !== void 0;
|
|
9
11
|
const selectedValue = isControlled ? value : internalValue;
|
|
12
|
+
const isDisabledResolved = isDisabled || pending;
|
|
10
13
|
const selectValue = (nextValue) => {
|
|
11
14
|
if (!isControlled) setInternalValue(nextValue);
|
|
12
15
|
onChange?.({ target: { value: nextValue } });
|
|
13
16
|
};
|
|
14
17
|
return /* @__PURE__ */ jsx("div", {
|
|
15
18
|
"aria-labelledby": labelId,
|
|
16
|
-
className: cn("flex cursor-pointer flex-col gap-2",
|
|
19
|
+
className: cn("flex cursor-pointer flex-col gap-2", isDisabledResolved && "cursor-not-allowed"),
|
|
17
20
|
role: "radiogroup",
|
|
18
21
|
children: options.map((option) => /* @__PURE__ */ jsxs("label", {
|
|
19
|
-
className: cn("flex items-center gap-2 text-left",
|
|
22
|
+
className: cn("flex items-center gap-2 text-left", isDisabledResolved ? "cursor-not-allowed" : "cursor-pointer"),
|
|
20
23
|
children: [
|
|
21
24
|
/* @__PURE__ */ jsx("input", {
|
|
22
25
|
checked: isControlled ? value === option.value : void 0,
|
|
23
26
|
className: "peer sr-only",
|
|
24
27
|
defaultChecked: isControlled ? void 0 : defaultValue === option.value,
|
|
25
|
-
disabled:
|
|
28
|
+
disabled: isDisabledResolved,
|
|
26
29
|
name: name ?? labelId,
|
|
27
30
|
onChange: () => {
|
|
28
31
|
selectValue(option.value);
|
|
@@ -32,7 +35,7 @@ const Radio = ({ labelId, name, isDisabled, value, defaultValue, onChange, optio
|
|
|
32
35
|
}),
|
|
33
36
|
/* @__PURE__ */ jsx("span", {
|
|
34
37
|
"aria-hidden": true,
|
|
35
|
-
className: cn("inline-flex size-5 items-center justify-center rounded-full border-2 transition-colors", "peer-focus-visible:border-transparent peer-focus-visible:outline-hidden peer-focus-visible:ring-2 peer-focus-visible:ring-border-info", selectedValue === option.value ? "border-border-base bg-primary-bg" : "border-border-mute bg-bg-base",
|
|
38
|
+
className: cn("inline-flex size-5 items-center justify-center rounded-full border-2 transition-colors", "peer-focus-visible:border-transparent peer-focus-visible:outline-hidden peer-focus-visible:ring-2 peer-focus-visible:ring-border-info", selectedValue === option.value ? "border-border-base bg-primary-bg" : "border-border-mute bg-bg-base", isDisabledResolved && "border-border-mute bg-bg-mute"),
|
|
36
39
|
children: /* @__PURE__ */ jsx("span", { className: cn("size-2 rounded-full transition-opacity", selectedValue === option.value ? "bg-primary-border opacity-100" : "bg-transparent opacity-0") })
|
|
37
40
|
}),
|
|
38
41
|
/* @__PURE__ */ jsx("span", { children: option.label })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { useId, useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/form/radio-card/radio-card.tsx
|
|
6
6
|
const RadioCard = ({ labelId, name, isDisabled, isInvalid = false, options, value, defaultValue, onChange }) => {
|
|
7
7
|
const groupId = useId();
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
2
3
|
import { ChevronIcon } from "../../icons/lucide.mjs";
|
|
4
|
+
import { useFormStatus } from "react-dom";
|
|
3
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
6
|
//#region src/components/form/select/select.tsx
|
|
5
7
|
const Select = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, options, value, defaultValue, onChange }) => {
|
|
8
|
+
const { pending } = useFormStatus();
|
|
6
9
|
return /* @__PURE__ */ jsxs("div", {
|
|
7
10
|
className: "relative h-fit w-full",
|
|
8
11
|
children: [/* @__PURE__ */ jsx("select", {
|
|
@@ -11,7 +14,7 @@ const Select = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, op
|
|
|
11
14
|
"aria-required": isRequired,
|
|
12
15
|
className: cn("w-full appearance-none rounded-xl border border-border-base bg-bg-base px-3 py-2 text-fg-base", "aria-invalid:border-border-error", "disabled:cursor-not-allowed disabled:border-border-mute disabled:bg-bg-mute disabled:hover:bg-bg-mute", "focus-visible:border-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info"),
|
|
13
16
|
defaultValue,
|
|
14
|
-
disabled: isDisabled,
|
|
17
|
+
disabled: isDisabled || pending,
|
|
15
18
|
id,
|
|
16
19
|
name,
|
|
17
20
|
onChange,
|