@k8o/arte-odyssey 4.2.1 → 5.0.1
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.mjs +1 -1
- package/dist/components/buttons/link-button/link-button.mjs +1 -1
- package/dist/components/data-display/accordion/context.mjs +5 -6
- package/dist/components/data-display/card/card.mjs +1 -1
- package/dist/components/data-display/card/interactive-card.mjs +1 -1
- package/dist/components/form/autocomplete/autocomplete.mjs +8 -9
- package/dist/components/form/checkbox-card/checkbox-card.mjs +1 -1
- package/dist/components/form/checkbox-group/checkbox-group.mjs +8 -7
- package/dist/components/form/file-field/file-field.mjs +1 -1
- package/dist/components/form/number-field/number-field.mjs +3 -3
- package/dist/components/form/password-input/password-input.mjs +4 -6
- package/dist/components/form/radio-card/radio-card.mjs +1 -1
- package/dist/components/form/select/select.mjs +1 -1
- package/dist/components/form/slider/slider.mjs +7 -10
- package/dist/components/form/text-field/text-field.mjs +1 -1
- package/dist/components/form/textarea/textarea.mjs +1 -1
- package/dist/components/navigation/tabs/tabs.mjs +1 -1
- package/dist/components/overlays/modal/modal.mjs +1 -1
- package/dist/components/overlays/popover/hooks.mjs +3 -2
- package/dist/components/overlays/popover/popover.mjs +8 -16
- package/dist/hooks/breakpoint/index.d.mts +5 -0
- package/dist/hooks/breakpoint/index.mjs +24 -0
- package/dist/hooks/click-away/index.d.mts +1 -1
- package/dist/hooks/click-away/index.mjs +7 -5
- package/dist/hooks/controllable-state/index.d.mts +13 -0
- package/dist/hooks/controllable-state/index.mjs +21 -0
- package/dist/hooks/debounce/index.d.mts +6 -0
- package/dist/hooks/debounce/index.mjs +35 -0
- package/dist/hooks/disclosure/index.d.mts +10 -0
- package/dist/hooks/disclosure/index.mjs +14 -0
- package/dist/hooks/hover/index.d.mts +12 -0
- package/dist/hooks/hover/index.mjs +15 -0
- package/dist/hooks/index.d.mts +11 -1
- package/dist/hooks/index.mjs +11 -1
- package/dist/hooks/intersection-observer/index.d.mts +3 -0
- package/dist/hooks/intersection-observer/index.mjs +3 -0
- package/dist/hooks/intersection-observer/use-in-view.d.mts +12 -0
- package/dist/hooks/intersection-observer/use-in-view.mjs +17 -0
- package/dist/hooks/intersection-observer/use-intersection-observer.d.mts +12 -0
- package/dist/hooks/intersection-observer/use-intersection-observer.mjs +33 -0
- package/dist/hooks/local-storage/index.mjs +5 -4
- package/dist/hooks/resize/index.d.mts +1 -1
- package/dist/hooks/resize/index.mjs +2 -3
- package/dist/hooks/scroll-direction/index.mjs +47 -22
- package/dist/hooks/scroll-lock/index.d.mts +8 -0
- package/dist/hooks/scroll-lock/index.mjs +44 -0
- package/dist/hooks/session-storage/index.d.mts +4 -0
- package/dist/hooks/session-storage/index.mjs +46 -0
- package/dist/hooks/throttle/index.d.mts +6 -0
- package/dist/hooks/throttle/index.mjs +53 -0
- package/dist/hooks/window-size/index.mjs +26 -19
- package/dist/index.d.mts +11 -1
- package/dist/index.mjs +11 -1
- package/dist/styles/index.css +186 -137
- package/docs/GUIDE.md +52 -33
- package/docs/references/color.md +71 -36
- package/docs/references/components.md +555 -181
- package/docs/references/helpers.md +120 -0
- package/docs/references/hooks.md +295 -0
- package/docs/references/interaction-design.md +24 -6
- package/docs/references/spatial-design.md +69 -33
- package/docs/references/typography.md +31 -17
- package/package.json +18 -18
|
@@ -3,7 +3,7 @@ import { jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
//#region src/components/buttons/button/button.tsx
|
|
4
4
|
const Button = ({ ref, children, type = "button", size = "md", color = "primary", variant = "contained", disabled = false, fullWidth = false, onClick, startIcon, endIcon, ...rest }) => {
|
|
5
5
|
return /* @__PURE__ */ jsxs("button", {
|
|
6
|
-
className: cn("cursor-pointer rounded-
|
|
6
|
+
className: cn("cursor-pointer rounded-full border-2 text-center font-bold transition-colors", {
|
|
7
7
|
"border-transparent bg-primary-bg text-fg hover:bg-primary-bg/90 active:bg-primary-bg/80": variant === "contained" && color === "primary",
|
|
8
8
|
"border-transparent bg-bg-subtle text-fg-base hover:bg-bg-mute active:bg-bg-emphasize": variant === "contained" && color === "gray",
|
|
9
9
|
"cursor-not-allowed opacity-35 hover:bg-primary-bg active:bg-primary-bg": disabled && variant === "contained",
|
|
@@ -12,7 +12,7 @@ const LinkButton = ({ children, size = "md", color = "primary", variant = "conta
|
|
|
12
12
|
};
|
|
13
13
|
return renderAnchor({
|
|
14
14
|
href,
|
|
15
|
-
className: cn("rounded-
|
|
15
|
+
className: cn("rounded-full border-2 text-center font-bold transition-colors", {
|
|
16
16
|
"border-transparent bg-primary-bg text-fg hover:bg-primary-bg/90 active:bg-primary-bg/80": variant === "contained" && color === "primary",
|
|
17
17
|
"border-transparent bg-bg-subtle text-fg-base hover:bg-bg-mute active:bg-bg-emphasize": variant === "contained" && color === "gray",
|
|
18
18
|
"border-primary-border bg-bg-base text-primary-fg hover:bg-bg-subtle active:bg-bg-emphasize": variant === "outlined" && color === "primary",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useDisclosure } from "../../../hooks/disclosure/index.mjs";
|
|
2
3
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, use
|
|
4
|
+
import { createContext, use } from "react";
|
|
4
5
|
//#region src/components/data-display/accordion/context.tsx
|
|
5
6
|
const OpenContext = createContext(false);
|
|
6
7
|
const ToggleOpenContext = createContext(void 0);
|
|
@@ -17,13 +18,11 @@ const useItemId = () => {
|
|
|
17
18
|
return id;
|
|
18
19
|
};
|
|
19
20
|
const AccordionItemProvider = ({ defaultOpen = false, id, children }) => {
|
|
20
|
-
const
|
|
21
|
+
const { isOpen, toggle } = useDisclosure(defaultOpen);
|
|
21
22
|
return /* @__PURE__ */ jsx(OpenContext, {
|
|
22
|
-
value:
|
|
23
|
+
value: isOpen,
|
|
23
24
|
children: /* @__PURE__ */ jsx(ToggleOpenContext, {
|
|
24
|
-
value:
|
|
25
|
-
setOpen((open) => !open);
|
|
26
|
-
}, []),
|
|
25
|
+
value: toggle,
|
|
27
26
|
children: /* @__PURE__ */ jsx(ItemIdContext, {
|
|
28
27
|
value: id,
|
|
29
28
|
children
|
|
@@ -2,7 +2,7 @@ import { cn } from "../../../helpers/cn.mjs";
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
//#region src/components/data-display/card/card.tsx
|
|
4
4
|
const Card = ({ children, variant = "primary", width = "full", appearance = "shadow" }) => /* @__PURE__ */ jsx("div", {
|
|
5
|
-
className: cn("rounded-
|
|
5
|
+
className: cn("rounded-xl", appearance === "shadow" && "shadow-sm", appearance === "bordered" && "border border-border-mute", width === "full" && "w-full", width === "fit" && "w-fit", variant === "primary" && "bg-bg-base", variant === "secondary" && "bg-bg-mute"),
|
|
6
6
|
children
|
|
7
7
|
});
|
|
8
8
|
//#endregion
|
|
@@ -2,7 +2,7 @@ import { cn } from "../../../helpers/cn.mjs";
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
//#region src/components/data-display/card/interactive-card.tsx
|
|
4
4
|
const InteractiveCard = ({ children, variant = "primary", width = "full", appearance = "shadow" }) => /* @__PURE__ */ jsx("div", {
|
|
5
|
-
className: cn("rounded-
|
|
5
|
+
className: cn("rounded-xl transition-transform hover:scale-[1.02] active:scale-[0.98]", appearance === "shadow" && "shadow-sm", appearance === "bordered" && "border border-border-mute", width === "full" && "w-full", width === "fit" && "w-fit", variant === "primary" && "bg-bg-base", variant === "secondary" && "bg-bg-mute"),
|
|
6
6
|
children
|
|
7
7
|
});
|
|
8
8
|
//#endregion
|
|
@@ -2,21 +2,20 @@
|
|
|
2
2
|
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
|
+
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
7
8
|
//#region src/components/form/autocomplete/autocomplete.tsx
|
|
8
9
|
const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, options, value, defaultValue, onChange }) => {
|
|
9
|
-
const [
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const [currentValue, handleChange] = useControllableState({
|
|
11
|
+
value,
|
|
12
|
+
defaultValue: defaultValue || [],
|
|
13
|
+
onChange
|
|
14
|
+
});
|
|
12
15
|
const ref = useRef(null);
|
|
13
16
|
const [open, setOpen] = useState(false);
|
|
14
17
|
const [text, setText] = useState("");
|
|
15
18
|
const [selectIndex, setSelectIndex] = useState();
|
|
16
|
-
const handleChange = useCallback((newValue) => {
|
|
17
|
-
if (!isControlled) setInternalValue(newValue);
|
|
18
|
-
onChange?.(newValue);
|
|
19
|
-
}, [isControlled, onChange]);
|
|
20
19
|
const filteredOptions = options.filter((option) => option.label.includes(text));
|
|
21
20
|
const reset = useCallback(() => {
|
|
22
21
|
setText("");
|
|
@@ -34,7 +33,7 @@ const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequir
|
|
|
34
33
|
};
|
|
35
34
|
}, [reset]);
|
|
36
35
|
return /* @__PURE__ */ jsxs("div", {
|
|
37
|
-
className: cn("relative w-full rounded-
|
|
36
|
+
className: cn("relative w-full rounded-xl border border-border-base bg-bg-base", "focus-within:border-transparent focus-within:outline-hidden focus-within:ring-2 focus-within:ring-border-info", "has-aria-invalid:border-border-error", "has-disabled:cursor-not-allowed has-disabled:border-border-mute has-disabled:bg-bg-mute has-disabled:has-hover:hover:bg-bg-mute"),
|
|
38
37
|
ref,
|
|
39
38
|
children: [
|
|
40
39
|
name ? currentValue.map((selectedValue) => /* @__PURE__ */ jsx("input", {
|
|
@@ -143,7 +142,7 @@ const Autocomplete = ({ id, name, describedbyId, isInvalid, isDisabled, isRequir
|
|
|
143
142
|
/* @__PURE__ */ jsx("div", {
|
|
144
143
|
className: "relative w-full",
|
|
145
144
|
children: open && /* @__PURE__ */ jsx("div", {
|
|
146
|
-
className: "absolute top-1 z-10 w-full rounded-
|
|
145
|
+
className: "absolute top-1 z-10 w-full rounded-xl border border-border-mute bg-bg-base shadow-md",
|
|
147
146
|
role: "presentation",
|
|
148
147
|
children: /* @__PURE__ */ jsxs("ul", {
|
|
149
148
|
className: "max-h-96 py-2",
|
|
@@ -22,7 +22,7 @@ const CheckboxCard = ({ labelId, name, isDisabled, isInvalid = false, options, v
|
|
|
22
22
|
const disabled = isDisabled || option.disabled;
|
|
23
23
|
const optionId = `${groupId}-${option.value}`;
|
|
24
24
|
return /* @__PURE__ */ jsxs("label", {
|
|
25
|
-
className: cn("flex w-full min-w-0 rounded-
|
|
25
|
+
className: cn("flex w-full min-w-0 rounded-xl border bg-bg-base p-4 text-left transition-colors", "has-[input:focus-visible]:outline-hidden has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-border-info", checked && "border-border-info bg-bg-subtle", isInvalid ? "border-border-error" : "border-border-mute hover:bg-bg-mute", disabled && "cursor-not-allowed border-border-mute bg-bg-subtle text-fg-mute"),
|
|
26
26
|
id: optionId,
|
|
27
27
|
children: [
|
|
28
28
|
/* @__PURE__ */ jsx("input", {
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
+
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import { createContext, use
|
|
5
|
+
import { createContext, use } from "react";
|
|
5
6
|
//#region src/components/form/checkbox-group/checkbox-group.tsx
|
|
6
7
|
const CheckboxGroupContext = createContext(void 0);
|
|
7
8
|
const useCheckboxGroupContext = () => use(CheckboxGroupContext);
|
|
8
9
|
const Root = ({ children, describedbyId, defaultValue, isDisabled = false, isInvalid = false, isRequired = false, labelId, name, onChange, value }) => {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const [currentValue, setCurrentValue] = useControllableState({
|
|
11
|
+
value,
|
|
12
|
+
defaultValue: defaultValue ?? [],
|
|
13
|
+
onChange
|
|
14
|
+
});
|
|
12
15
|
const toggleValue = (targetValue) => {
|
|
13
|
-
|
|
14
|
-
if (!isControlled) setInternalValue(nextValue);
|
|
15
|
-
onChange?.(nextValue);
|
|
16
|
+
setCurrentValue(currentValue.includes(targetValue) ? currentValue.filter((item) => item !== targetValue) : [...currentValue, targetValue]);
|
|
16
17
|
};
|
|
17
18
|
return /* @__PURE__ */ jsx("fieldset", {
|
|
18
19
|
"aria-describedby": describedbyId,
|
|
@@ -100,7 +100,7 @@ const ItemList = ({ showWebkitRelativePath, clearable }) => {
|
|
|
100
100
|
const onDelete = () => onFileDelete(id);
|
|
101
101
|
const sizeInKB = (file.size / 1024).toFixed(2);
|
|
102
102
|
return /* @__PURE__ */ jsxs("li", {
|
|
103
|
-
className: "flex items-center justify-between rounded-
|
|
103
|
+
className: "flex items-center justify-between rounded-xl border border-border-base bg-bg-base px-3 py-2",
|
|
104
104
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
105
105
|
className: "flex flex-col gap-1",
|
|
106
106
|
children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -23,7 +23,7 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
23
23
|
onChange?.(newValue);
|
|
24
24
|
};
|
|
25
25
|
return /* @__PURE__ */ jsxs("div", {
|
|
26
|
-
className: cn("relative flex h-12 w-full items-center justify-between gap-2 rounded-
|
|
26
|
+
className: cn("relative flex h-12 w-full items-center justify-between gap-2 rounded-xl border border-border-base bg-bg-base", "focus-within:border-transparent focus-within:outline-hidden focus-within:ring-2 focus-within:ring-border-info", "has-aria-invalid:border-border-error", "has-disabled:cursor-not-allowed has-disabled:border-border-mute has-disabled:bg-bg-mute has-disabled:has-hover:hover:bg-bg-mute"),
|
|
27
27
|
children: [/* @__PURE__ */ jsx("input", {
|
|
28
28
|
"aria-describedby": describedbyId,
|
|
29
29
|
"aria-invalid": isInvalid,
|
|
@@ -68,7 +68,7 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
68
68
|
"aria-hidden": "true",
|
|
69
69
|
className: "absolute right-0 flex h-full flex-col",
|
|
70
70
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
71
|
-
className: cn("flex w-6 grow items-center justify-center rounded-tr-
|
|
71
|
+
className: cn("flex w-6 grow items-center justify-center rounded-tr-xl border-border-base border-b border-l bg-bg-mute", "disabled:cursor-not-allowed"),
|
|
72
72
|
disabled: isDisabled,
|
|
73
73
|
onClick: () => {
|
|
74
74
|
const newValue = between(toPrecision(cast(displayValue, precision) + step, precision), min, max);
|
|
@@ -82,7 +82,7 @@ const NumberField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequire
|
|
|
82
82
|
children: "増やす"
|
|
83
83
|
}), /* @__PURE__ */ jsx(PlusIcon, { size: "sm" })]
|
|
84
84
|
}), /* @__PURE__ */ jsxs("button", {
|
|
85
|
-
className: cn("flex w-6 grow items-center justify-center rounded-br-
|
|
85
|
+
className: cn("flex w-6 grow items-center justify-center rounded-br-xl border-border-base border-l bg-bg-mute", "disabled:cursor-not-allowed"),
|
|
86
86
|
disabled: isDisabled,
|
|
87
87
|
onClick: () => {
|
|
88
88
|
const newValue = between(toPrecision(cast(displayValue, precision) - step, precision), min, max);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
3
|
import { ViewIcon, ViewOffIcon } from "../../icons/lucide.mjs";
|
|
4
|
+
import { useDisclosure } from "../../../hooks/disclosure/index.mjs";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
import { useState } from "react";
|
|
6
6
|
//#region src/components/form/password-input/password-input.tsx
|
|
7
7
|
const PasswordInput = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, placeholder, autoComplete = "current-password", showLabel = "Show password", hideLabel = "Hide password", defaultValue, value, onChange }) => {
|
|
8
|
-
const
|
|
8
|
+
const { isOpen: isVisible, toggle: toggleVisible } = useDisclosure();
|
|
9
9
|
return /* @__PURE__ */ jsxs("div", {
|
|
10
10
|
className: "relative w-full",
|
|
11
11
|
children: [/* @__PURE__ */ jsx("input", {
|
|
@@ -13,7 +13,7 @@ const PasswordInput = ({ id, name, describedbyId, isInvalid, isDisabled, isRequi
|
|
|
13
13
|
"aria-invalid": isInvalid,
|
|
14
14
|
"aria-required": isRequired,
|
|
15
15
|
autoComplete,
|
|
16
|
-
className: cn("w-full rounded-
|
|
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"),
|
|
17
17
|
defaultValue,
|
|
18
18
|
disabled: isDisabled,
|
|
19
19
|
id,
|
|
@@ -27,9 +27,7 @@ const PasswordInput = ({ id, name, describedbyId, isInvalid, isDisabled, isRequi
|
|
|
27
27
|
"aria-label": isVisible ? hideLabel : showLabel,
|
|
28
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
29
|
disabled: isDisabled,
|
|
30
|
-
onClick:
|
|
31
|
-
setIsVisible((current) => !current);
|
|
32
|
-
},
|
|
30
|
+
onClick: toggleVisible,
|
|
33
31
|
type: "button",
|
|
34
32
|
children: isVisible ? /* @__PURE__ */ jsx(ViewOffIcon, { size: "sm" }) : /* @__PURE__ */ jsx(ViewIcon, { size: "sm" })
|
|
35
33
|
})]
|
|
@@ -36,7 +36,7 @@ const RadioCard = ({ labelId, name, isDisabled, isInvalid = false, options, valu
|
|
|
36
36
|
return /* @__PURE__ */ jsxs("button", {
|
|
37
37
|
"aria-describedby": option.description ? `${optionId}-description` : void 0,
|
|
38
38
|
"aria-pressed": checked,
|
|
39
|
-
className: cn("flex w-full min-w-0 rounded-
|
|
39
|
+
className: cn("flex w-full min-w-0 rounded-xl border bg-bg-base p-4 text-left transition-colors", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info", checked && "border-border-info bg-bg-subtle", isInvalid ? "border-border-error" : "border-border-mute hover:bg-bg-mute", disabled && "cursor-not-allowed border-border-mute bg-bg-subtle text-fg-mute"),
|
|
40
40
|
disabled,
|
|
41
41
|
id: optionId,
|
|
42
42
|
onClick: () => {
|
|
@@ -9,7 +9,7 @@ const Select = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, op
|
|
|
9
9
|
"aria-describedby": describedbyId,
|
|
10
10
|
"aria-invalid": isInvalid,
|
|
11
11
|
"aria-required": isRequired,
|
|
12
|
-
className: cn("w-full appearance-none rounded-
|
|
12
|
+
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
13
|
defaultValue,
|
|
14
14
|
disabled: isDisabled,
|
|
15
15
|
id,
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../../helpers/cn.mjs";
|
|
3
|
+
import { useControllableState } from "../../../hooks/controllable-state/index.mjs";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import { useState } from "react";
|
|
5
5
|
//#region src/components/form/slider/slider.tsx
|
|
6
6
|
const Slider = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, value, defaultValue, onChange, step = 1, max = 100, min = 0 }) => {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const [currentValue, handleChange] = useControllableState({
|
|
8
|
+
value,
|
|
9
|
+
defaultValue: defaultValue ?? min,
|
|
10
|
+
onChange
|
|
11
|
+
});
|
|
10
12
|
const range = Math.max(max - min, 1);
|
|
11
13
|
const progress = (currentValue - min) / range * 100;
|
|
12
14
|
const style = { "--slider-progress": `${Math.min(Math.max(progress, 0), 100)}%` };
|
|
13
|
-
const handleChange = (newValue) => {
|
|
14
|
-
if (!isControlled) setInternalValue(newValue);
|
|
15
|
-
onChange?.(newValue);
|
|
16
|
-
};
|
|
17
15
|
return /* @__PURE__ */ jsx("div", {
|
|
18
16
|
className: cn("relative flex h-8 w-full items-center", "before:absolute before:inset-x-0 before:h-2 before:rounded-full before:bg-bg-mute", "after:absolute after:left-0 after:h-2 after:rounded-full after:bg-primary-bg", "after:w-(--slider-progress)", isInvalid && "after:bg-bg-error", isDisabled && "opacity-50"),
|
|
19
17
|
style,
|
|
@@ -24,7 +22,6 @@ const Slider = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, va
|
|
|
24
22
|
"aria-valuemin": min,
|
|
25
23
|
"aria-valuenow": currentValue,
|
|
26
24
|
className: cn("relative z-10 h-8 w-full appearance-none bg-transparent", "focus:outline-none", "disabled:cursor-not-allowed", "[&::-webkit-slider-runnable-track]:h-2 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-transparent", "[&::-webkit-slider-thumb]:mt-[-4px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-border-base [&::-webkit-slider-thumb]:bg-bg-base [&::-webkit-slider-thumb]:shadow-xs", "[&:focus-visible::-webkit-slider-thumb]:border-transparent [&:focus-visible::-webkit-slider-thumb]:ring-2 [&:focus-visible::-webkit-slider-thumb]:ring-border-info", "[&::-moz-range-track]:h-2 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-transparent", "[&::-moz-range-progress]:h-2 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-transparent", "[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-border-base [&::-moz-range-thumb]:bg-bg-base [&::-moz-range-thumb]:shadow-xs", "[&:focus-visible::-moz-range-thumb]:border-transparent [&:focus-visible::-moz-range-thumb]:ring-2 [&:focus-visible::-moz-range-thumb]:ring-border-info", isInvalid && "[&::-moz-range-thumb]:border-border-error [&::-webkit-slider-thumb]:border-border-error [&:focus-visible::-moz-range-thumb]:ring-border-error [&:focus-visible::-webkit-slider-thumb]:ring-border-error"),
|
|
27
|
-
defaultValue: isControlled ? void 0 : defaultValue,
|
|
28
25
|
disabled: isDisabled,
|
|
29
26
|
id,
|
|
30
27
|
max,
|
|
@@ -36,7 +33,7 @@ const Slider = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired, va
|
|
|
36
33
|
required: isRequired,
|
|
37
34
|
step,
|
|
38
35
|
type: "range",
|
|
39
|
-
value:
|
|
36
|
+
value: currentValue
|
|
40
37
|
})
|
|
41
38
|
});
|
|
42
39
|
};
|
|
@@ -6,7 +6,7 @@ const TextField = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired,
|
|
|
6
6
|
"aria-describedby": describedbyId,
|
|
7
7
|
"aria-invalid": isInvalid,
|
|
8
8
|
"aria-required": isRequired,
|
|
9
|
-
className: cn("w-full rounded-
|
|
9
|
+
className: cn("w-full rounded-xl border border-border-base bg-bg-base px-3 py-2", "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"),
|
|
10
10
|
defaultValue,
|
|
11
11
|
disabled: isDisabled,
|
|
12
12
|
id,
|
|
@@ -15,7 +15,7 @@ const Textarea = ({ id, name, describedbyId, isInvalid, isDisabled, isRequired,
|
|
|
15
15
|
"aria-describedby": describedbyId,
|
|
16
16
|
"aria-invalid": isInvalid,
|
|
17
17
|
"aria-required": isRequired,
|
|
18
|
-
className: cn("w-full resize-none rounded-
|
|
18
|
+
className: cn("w-full resize-none rounded-xl border border-border-base bg-bg-base px-3 py-2", "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", fullHeight && "h-full"),
|
|
19
19
|
defaultValue,
|
|
20
20
|
disabled: isDisabled,
|
|
21
21
|
id,
|
|
@@ -88,7 +88,7 @@ const Tab = ({ id, children }) => {
|
|
|
88
88
|
tabIndex: activeIndex === index ? 0 : -1,
|
|
89
89
|
children: [selectedId === id && /* @__PURE__ */ jsx(motion.div, {
|
|
90
90
|
className: "absolute right-0 -bottom-0.5 left-0 h-1 bg-primary-border",
|
|
91
|
-
layoutId:
|
|
91
|
+
layoutId: `${rootId}-underline`
|
|
92
92
|
}), children]
|
|
93
93
|
});
|
|
94
94
|
};
|
|
@@ -140,7 +140,7 @@ const Modal = ({ ref, type = "center", defaultOpen, isOpen, onClose, children })
|
|
|
140
140
|
]);
|
|
141
141
|
return /* @__PURE__ */ jsx(motion.dialog, {
|
|
142
142
|
animate: realDialogOpen ? "open" : "closed",
|
|
143
|
-
className: cn("border-border-mute bg-bg-base shadow-md backdrop:bg-back-drop", type === "center" && "m-auto max-h-lg w-5/6 max-w-2xl rounded-lg dark:border", type === "bottom" && "mt-auto w-screen max-w-screen rounded-t-lg dark:border-t", type === "right" && "ml-auto h-svh max-h-none w-screen max-w-sm rounded-l-lg dark:border-l", type === "left" && "mr-auto h-svh max-h-none w-screen max-w-sm rounded-r-lg dark:border-r"),
|
|
143
|
+
className: cn("border-border-mute bg-bg-base text-fg-base shadow-md backdrop:bg-back-drop", type === "center" && "m-auto max-h-lg w-5/6 max-w-2xl rounded-lg dark:border", type === "bottom" && "mt-auto w-screen max-w-screen rounded-t-lg dark:border-t", type === "right" && "ml-auto h-svh max-h-none w-screen max-w-sm rounded-l-lg dark:border-l", type === "left" && "mr-auto h-svh max-h-none w-screen max-w-sm rounded-r-lg dark:border-r"),
|
|
144
144
|
exit: "closed",
|
|
145
145
|
initial: "closed",
|
|
146
146
|
onClick: (e) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useClickAway } from "../../../hooks/click-away/index.mjs";
|
|
3
|
-
import { createContext, use, useMemo } from "react";
|
|
3
|
+
import { createContext, use, useMemo, useRef } from "react";
|
|
4
4
|
//#region src/components/overlays/popover/hooks.ts
|
|
5
5
|
const PopoverContext = createContext(null);
|
|
6
6
|
const PopoverProvider = PopoverContext;
|
|
@@ -33,7 +33,8 @@ const useOpenContext = () => {
|
|
|
33
33
|
const usePopoverContent = () => {
|
|
34
34
|
const popover = usePopoverContext();
|
|
35
35
|
const isHover = popover.type === "tooltip";
|
|
36
|
-
const ref =
|
|
36
|
+
const ref = useRef(null);
|
|
37
|
+
useClickAway(ref, (event) => {
|
|
37
38
|
if (!popover.isOpen) return;
|
|
38
39
|
if (popover.triggerRef.current?.contains(event.target)) return;
|
|
39
40
|
popover.onClose();
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { useDisclosure } from "../../../hooks/disclosure/index.mjs";
|
|
2
3
|
import { usePortalRoot } from "../../providers/portal-root.mjs";
|
|
3
4
|
import { PopoverProvider, useOpenContext, usePopoverContent, usePopoverTrigger } from "./hooks.mjs";
|
|
4
5
|
import { jsx } from "react/jsx-runtime";
|
|
5
|
-
import {
|
|
6
|
+
import { useEffect, useId } from "react";
|
|
6
7
|
import { AnimatePresence } from "motion/react";
|
|
7
8
|
import * as motion$1 from "motion/react-client";
|
|
8
9
|
import { FloatingFocusManager, FloatingPortal, autoUpdate, flip, offset, useFloating } from "@floating-ui/react";
|
|
9
10
|
//#region src/components/overlays/popover/popover.tsx
|
|
10
11
|
const Root = ({ children, type = "menu", placement = "bottom-start", flipDisabled = false }) => {
|
|
11
12
|
const id = useId();
|
|
12
|
-
const
|
|
13
|
+
const { isOpen, open, close, toggle } = useDisclosure();
|
|
13
14
|
const { refs, floatingStyles, context, placement: computedPlacement } = useFloating({
|
|
14
15
|
strategy: "fixed",
|
|
15
16
|
placement,
|
|
@@ -21,32 +22,23 @@ const Root = ({ children, type = "menu", placement = "bottom-start", flipDisable
|
|
|
21
22
|
})],
|
|
22
23
|
transform: false
|
|
23
24
|
});
|
|
24
|
-
const toggleOpen = useCallback(() => {
|
|
25
|
-
setIsOpen((prev) => !prev);
|
|
26
|
-
}, []);
|
|
27
|
-
const onOpen = useCallback(() => {
|
|
28
|
-
setIsOpen(true);
|
|
29
|
-
}, []);
|
|
30
|
-
const onClose = useCallback(() => {
|
|
31
|
-
setIsOpen(false);
|
|
32
|
-
}, []);
|
|
33
25
|
useEffect(() => {
|
|
34
26
|
const handleKeyDown = (e) => {
|
|
35
|
-
if (e.key === "Escape")
|
|
27
|
+
if (e.key === "Escape") close();
|
|
36
28
|
};
|
|
37
29
|
window.addEventListener("keydown", handleKeyDown);
|
|
38
30
|
return () => {
|
|
39
31
|
window.removeEventListener("keydown", handleKeyDown);
|
|
40
32
|
};
|
|
41
|
-
}, [
|
|
33
|
+
}, [close]);
|
|
42
34
|
return /* @__PURE__ */ jsx(PopoverProvider, {
|
|
43
35
|
value: {
|
|
44
36
|
rootId: id,
|
|
45
37
|
type,
|
|
46
38
|
isOpen,
|
|
47
|
-
toggleOpen,
|
|
48
|
-
onOpen,
|
|
49
|
-
onClose,
|
|
39
|
+
toggleOpen: toggle,
|
|
40
|
+
onOpen: open,
|
|
41
|
+
onClose: close,
|
|
50
42
|
context,
|
|
51
43
|
placement: computedPlacement,
|
|
52
44
|
triggerRef: refs.domReference,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
3
|
+
//#region src/hooks/breakpoint/index.ts
|
|
4
|
+
const BREAKPOINTS = {
|
|
5
|
+
sm: "40rem",
|
|
6
|
+
md: "48rem",
|
|
7
|
+
lg: "64rem",
|
|
8
|
+
xl: "80rem",
|
|
9
|
+
"2xl": "96rem"
|
|
10
|
+
};
|
|
11
|
+
const useBreakpoint = (breakpoint) => {
|
|
12
|
+
const query = `(min-width: ${BREAKPOINTS[breakpoint]})`;
|
|
13
|
+
const subscribe = useCallback((cb) => {
|
|
14
|
+
const mediaQueryList = window.matchMedia(query);
|
|
15
|
+
mediaQueryList.addEventListener("change", cb);
|
|
16
|
+
return () => {
|
|
17
|
+
mediaQueryList.removeEventListener("change", cb);
|
|
18
|
+
};
|
|
19
|
+
}, [query]);
|
|
20
|
+
const getSnapshot = () => window.matchMedia(query).matches;
|
|
21
|
+
return useSyncExternalStore(subscribe, getSnapshot, () => false);
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { useBreakpoint };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
2
|
|
|
3
3
|
//#region src/hooks/click-away/index.d.ts
|
|
4
|
-
declare const useClickAway: <T extends Element = HTMLElement>(callback: (e: Event) => void, enabled?: boolean) =>
|
|
4
|
+
declare const useClickAway: <T extends Element = HTMLElement>(ref: RefObject<T | null>, callback: (e: Event) => void, enabled?: boolean) => void;
|
|
5
5
|
//#endregion
|
|
6
6
|
export { useClickAway };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useEffect
|
|
2
|
+
import { useEffect } from "react";
|
|
3
3
|
//#region src/hooks/click-away/index.ts
|
|
4
|
-
const useClickAway = (callback, enabled = true) => {
|
|
5
|
-
const ref = useRef(null);
|
|
4
|
+
const useClickAway = (ref, callback, enabled = true) => {
|
|
6
5
|
useEffect(() => {
|
|
7
6
|
if (!enabled) return;
|
|
8
7
|
const handler = (e) => {
|
|
@@ -15,8 +14,11 @@ const useClickAway = (callback, enabled = true) => {
|
|
|
15
14
|
document.removeEventListener("mousedown", handler);
|
|
16
15
|
document.removeEventListener("touchstart", handler);
|
|
17
16
|
};
|
|
18
|
-
}, [
|
|
19
|
-
|
|
17
|
+
}, [
|
|
18
|
+
ref,
|
|
19
|
+
callback,
|
|
20
|
+
enabled
|
|
21
|
+
]);
|
|
20
22
|
};
|
|
21
23
|
//#endregion
|
|
22
24
|
export { useClickAway };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/hooks/controllable-state/index.d.ts
|
|
2
|
+
type UseControllableStateProps<T> = {
|
|
3
|
+
value?: T;
|
|
4
|
+
defaultValue: T;
|
|
5
|
+
onChange?: (value: T) => void;
|
|
6
|
+
};
|
|
7
|
+
declare const useControllableState: <T>({
|
|
8
|
+
value,
|
|
9
|
+
defaultValue,
|
|
10
|
+
onChange
|
|
11
|
+
}: UseControllableStateProps<T>) => [T, (next: T | ((prev: T) => T)) => void];
|
|
12
|
+
//#endregion
|
|
13
|
+
export { useControllableState };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useRef, useState } from "react";
|
|
3
|
+
//#region src/hooks/controllable-state/index.ts
|
|
4
|
+
const useControllableState = ({ value, defaultValue, onChange }) => {
|
|
5
|
+
const isControlled = value !== void 0;
|
|
6
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
7
|
+
const currentValue = isControlled ? value : internalValue;
|
|
8
|
+
const currentValueRef = useRef(currentValue);
|
|
9
|
+
currentValueRef.current = currentValue;
|
|
10
|
+
const onChangeRef = useRef(onChange);
|
|
11
|
+
onChangeRef.current = onChange;
|
|
12
|
+
const isControlledRef = useRef(isControlled);
|
|
13
|
+
isControlledRef.current = isControlled;
|
|
14
|
+
return [currentValue, useCallback((next) => {
|
|
15
|
+
const nextValue = typeof next === "function" ? next(currentValueRef.current) : next;
|
|
16
|
+
if (!isControlledRef.current) setInternalValue(nextValue);
|
|
17
|
+
onChangeRef.current?.(nextValue);
|
|
18
|
+
}, [])];
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { useControllableState };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/hooks/debounce/index.d.ts
|
|
2
|
+
type AnyFunction = (...args: any[]) => any;
|
|
3
|
+
declare const useDebounce: <T>(value: T, delay: number) => T;
|
|
4
|
+
declare const useDebouncedCallback: <T extends AnyFunction>(callback: T, delay: number) => (...args: Parameters<T>) => void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { useDebounce, useDebouncedCallback };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
//#region src/hooks/debounce/index.ts
|
|
4
|
+
const useDebounce = (value, delay) => {
|
|
5
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const timer = setTimeout(() => {
|
|
8
|
+
setDebouncedValue(value);
|
|
9
|
+
}, delay);
|
|
10
|
+
return () => {
|
|
11
|
+
clearTimeout(timer);
|
|
12
|
+
};
|
|
13
|
+
}, [value, delay]);
|
|
14
|
+
return debouncedValue;
|
|
15
|
+
};
|
|
16
|
+
const useDebouncedCallback = (callback, delay) => {
|
|
17
|
+
const timerRef = useRef(void 0);
|
|
18
|
+
const callbackRef = useRef(callback);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
callbackRef.current = callback;
|
|
21
|
+
});
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
return () => {
|
|
24
|
+
clearTimeout(timerRef.current);
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
return useCallback((...args) => {
|
|
28
|
+
clearTimeout(timerRef.current);
|
|
29
|
+
timerRef.current = setTimeout(() => {
|
|
30
|
+
callbackRef.current(...args);
|
|
31
|
+
}, delay);
|
|
32
|
+
}, [delay]);
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { useDebounce, useDebouncedCallback };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/hooks/disclosure/index.d.ts
|
|
2
|
+
type UseDisclosureReturn = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
open: () => void;
|
|
5
|
+
close: () => void;
|
|
6
|
+
toggle: () => void;
|
|
7
|
+
};
|
|
8
|
+
declare const useDisclosure: (defaultOpen?: boolean) => UseDisclosureReturn;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useDisclosure };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
//#region src/hooks/disclosure/index.ts
|
|
4
|
+
const useDisclosure = (defaultOpen = false) => {
|
|
5
|
+
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
6
|
+
return {
|
|
7
|
+
isOpen,
|
|
8
|
+
open: useCallback(() => setIsOpen(true), []),
|
|
9
|
+
close: useCallback(() => setIsOpen(false), []),
|
|
10
|
+
toggle: useCallback(() => setIsOpen((prev) => !prev), [])
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useDisclosure };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/hooks/hover/index.d.ts
|
|
2
|
+
type HoverProps = {
|
|
3
|
+
onPointerEnter: () => void;
|
|
4
|
+
onPointerLeave: () => void;
|
|
5
|
+
};
|
|
6
|
+
type UseHoverReturn = {
|
|
7
|
+
isHovered: boolean;
|
|
8
|
+
hoverProps: HoverProps;
|
|
9
|
+
};
|
|
10
|
+
declare const useHover: () => UseHoverReturn;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { useHover };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
//#region src/hooks/hover/index.ts
|
|
4
|
+
const useHover = () => {
|
|
5
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
6
|
+
return {
|
|
7
|
+
isHovered,
|
|
8
|
+
hoverProps: {
|
|
9
|
+
onPointerEnter: useCallback(() => setIsHovered(true), []),
|
|
10
|
+
onPointerLeave: useCallback(() => setIsHovered(false), [])
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { useHover };
|