@cystackapp/ui 1.4.0 → 1.5.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/README.md +105 -0
- package/dist/components/badge/Badge.js +3 -3
- package/dist/components/badge/variants/BadgeTag.js +1 -1
- package/dist/components/button/Button.d.ts +21 -0
- package/dist/components/button/Button.js +49 -0
- package/dist/components/button/ButtonLoader.d.ts +7 -0
- package/dist/components/button/ButtonLoader.js +44 -0
- package/dist/components/button/ButtonTestStory.d.ts +5 -0
- package/dist/components/button/button-variants.d.ts +13 -0
- package/dist/components/button/button-variants.js +58 -0
- package/dist/components/card/Card.d.ts +91 -0
- package/dist/components/card/Card.js +56 -0
- package/dist/components/card/Card.stories-ct.d.ts +1 -0
- package/dist/components/card/CardBody.d.ts +9 -0
- package/dist/components/card/CardBody.js +16 -0
- package/dist/components/card/CardHeader.d.ts +15 -0
- package/dist/components/card/CardHeader.js +79 -0
- package/dist/components/card/types.d.ts +1 -0
- package/dist/components/checkbox/Checkbox.d.ts +6 -0
- package/dist/components/checkbox/Checkbox.js +72 -0
- package/dist/components/checkbox/CheckboxTestStory.d.ts +7 -0
- package/dist/components/collapsible/Collapsible.d.ts +21 -0
- package/dist/components/collapsible/Collapsible.js +50 -0
- package/dist/components/input/Input.d.ts +11 -0
- package/dist/components/input/Input.js +39 -0
- package/dist/components/modal/Modal.d.ts +9 -0
- package/dist/components/modal/Modal.js +68 -0
- package/dist/components/modal/helpers/HeaderIcon.d.ts +8 -0
- package/dist/components/modal/helpers/HeaderIcon.js +32 -0
- package/dist/components/modal/helpers/Title.d.ts +6 -0
- package/dist/components/modal/helpers/Title.js +9 -0
- package/dist/components/modal/helpers/circles-sm.svg.js +5 -0
- package/dist/components/modal/modal-context.d.ts +2 -0
- package/dist/components/modal/modal-context.js +9 -0
- package/dist/components/modal/use-click-outside-modal.d.ts +2 -0
- package/dist/components/modal/use-click-outside-modal.js +15 -0
- package/dist/components/toast/ToastSlice.d.ts +11 -0
- package/dist/components/toast/ToastSlice.js +103 -0
- package/dist/components/toast/icons.d.ts +8 -0
- package/dist/components/toast/icons.js +19 -0
- package/dist/components/toast/index.d.ts +4 -0
- package/dist/components/toast/index.js +35 -0
- package/dist/components/toast/types.d.ts +14 -0
- package/dist/components/toast/use-toast-list.d.ts +4 -0
- package/dist/components/toast/use-toast-list.js +27 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +55 -34
- package/dist/test-utils/expect-visibility.d.ts +45 -0
- package/dist/utils/observable.d.ts +7 -0
- package/dist/utils/observable.js +22 -0
- package/package.json +3 -2
- package/theme.css +3 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsxs as l, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { Check as d, Minus as b } from "@untitled-ui/icons-react";
|
|
3
|
+
import { forwardRef as u } from "react";
|
|
4
|
+
import { cn as t } from "../../utils/cn.js";
|
|
5
|
+
const c = u(
|
|
6
|
+
({ checked: a, indeterminate: o, className: r, ...e }, n) => {
|
|
7
|
+
const i = !!o && !a;
|
|
8
|
+
return /* @__PURE__ */ l(
|
|
9
|
+
"label",
|
|
10
|
+
{
|
|
11
|
+
className: t(
|
|
12
|
+
"relative size-5 shrink-0 block rounded-md bg-white transition-all ease-out border overflow-hidden",
|
|
13
|
+
a || i ? e.disabled ? "border-gray-v2-300" : "border-brand-v2-600" : "border-gray-v2-300",
|
|
14
|
+
{ "bg-gray-v2-50": e.disabled },
|
|
15
|
+
r
|
|
16
|
+
),
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ s(
|
|
19
|
+
"input",
|
|
20
|
+
{
|
|
21
|
+
ref: n,
|
|
22
|
+
...e,
|
|
23
|
+
checked: a,
|
|
24
|
+
className: t(
|
|
25
|
+
"absolute top-0 left-0 z-10 size-full rounded-md appearance-none",
|
|
26
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-v2-400",
|
|
27
|
+
e.disabled ? "cursor-not-allowed" : "cursor-pointer"
|
|
28
|
+
),
|
|
29
|
+
type: "checkbox"
|
|
30
|
+
}
|
|
31
|
+
),
|
|
32
|
+
/* @__PURE__ */ l(
|
|
33
|
+
"span",
|
|
34
|
+
{
|
|
35
|
+
className: t(
|
|
36
|
+
"relative size-full transition-all ease-out rounded-0.5 flex justify-center items-center",
|
|
37
|
+
{ "bg-brand-v2-600": !e.disabled },
|
|
38
|
+
{ "scale-[40%] opacity-0 rotate-12": !(a || i) }
|
|
39
|
+
),
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ s(
|
|
42
|
+
d,
|
|
43
|
+
{
|
|
44
|
+
className: t(
|
|
45
|
+
"absolute size-[70%] transition-all ease-out",
|
|
46
|
+
e.disabled ? "text-gray-v2-300" : "text-white",
|
|
47
|
+
{ "scale-0": !a }
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
/* @__PURE__ */ s(
|
|
52
|
+
b,
|
|
53
|
+
{
|
|
54
|
+
className: t(
|
|
55
|
+
"absolute size-[70%] transition-all ease-out",
|
|
56
|
+
e.disabled ? "text-gray-v2-300" : "text-white",
|
|
57
|
+
{ "scale-0": !i }
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
c.displayName = "Checkbox";
|
|
70
|
+
export {
|
|
71
|
+
c as Checkbox
|
|
72
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function CheckboxUnchecked(): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function CheckboxChecked(): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function CheckboxIndeterminate(): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare function CheckboxCheckedAndIndeterminate(): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function CheckboxDisabled(): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function CheckboxDisabledChecked(): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function CheckboxWithClickCounter(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
expanded: boolean;
|
|
4
|
+
duration?: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Pure animated collapse container. No trigger, no label — just an outer
|
|
10
|
+
* grid whose row template animates between `0fr` and `1fr`, with a delayed
|
|
11
|
+
* `visibility` transition so collapsed content is unreachable by pointer,
|
|
12
|
+
* focus, and assistive tech once the fade finishes.
|
|
13
|
+
*
|
|
14
|
+
* Consumers own the toggle UI (e.g. `Accordion` wraps its own `role=button`
|
|
15
|
+
* label around this; `Card` keeps its collapse button in `CardHeader`).
|
|
16
|
+
*
|
|
17
|
+
* @param expanded - Whether the content is shown.
|
|
18
|
+
* @param duration - Transition duration in milliseconds (default: 300).
|
|
19
|
+
*/
|
|
20
|
+
export declare const Collapsible: ({ expanded, duration, className, children, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { useState as f, useEffect as c } from "react";
|
|
3
|
+
import { cn as s } from "../../utils/cn.js";
|
|
4
|
+
const g = ({
|
|
5
|
+
expanded: i,
|
|
6
|
+
duration: n = 300,
|
|
7
|
+
className: e,
|
|
8
|
+
children: a
|
|
9
|
+
}) => {
|
|
10
|
+
const t = `${n}ms`, [l, o] = f(
|
|
11
|
+
i ? "visible" : "hidden"
|
|
12
|
+
);
|
|
13
|
+
return c(() => {
|
|
14
|
+
i && o("visible");
|
|
15
|
+
}, [i]), /* @__PURE__ */ r(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
className: s(
|
|
19
|
+
"grid motion-safe:transition-[grid-template-rows] ease-out",
|
|
20
|
+
i ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
|
|
21
|
+
e
|
|
22
|
+
),
|
|
23
|
+
style: { transitionDuration: t },
|
|
24
|
+
onTransitionEnd: (m) => {
|
|
25
|
+
m.propertyName === "grid-template-rows" && !i && o("hidden");
|
|
26
|
+
},
|
|
27
|
+
children: /* @__PURE__ */ r(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
className: s("min-h-0", {
|
|
31
|
+
"invisible opacity-0": !i
|
|
32
|
+
}),
|
|
33
|
+
style: {
|
|
34
|
+
overflow: l,
|
|
35
|
+
transitionProperty: "opacity, visibility",
|
|
36
|
+
transitionDuration: `${t}, 0s`,
|
|
37
|
+
transitionTimingFunction: "ease-out",
|
|
38
|
+
// Delay visibility so it hides only after the fade finishes.
|
|
39
|
+
// When expanding, no delay — content becomes visible immediately.
|
|
40
|
+
transitionDelay: i ? "0s, 0s" : `0s, ${t}`
|
|
41
|
+
},
|
|
42
|
+
children: a
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
export {
|
|
49
|
+
g as Collapsible
|
|
50
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
interface Props extends Omit<InputHTMLAttributes<HTMLInputElement>, "prefix"> {
|
|
3
|
+
/** Marks the input as in an error state — switches the focus ring to error-v2. */
|
|
4
|
+
invalid?: boolean;
|
|
5
|
+
/** Content rendered before the input (e.g. an icon). */
|
|
6
|
+
prefix?: ReactNode;
|
|
7
|
+
/** Content rendered after the input (e.g. a unit label or clear button). */
|
|
8
|
+
suffix?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const Input: import('react').ForwardRefExoticComponent<Props & import('react').RefAttributes<HTMLInputElement>>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsxs as d, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as u } from "react";
|
|
3
|
+
import { cn as c } from "../../utils/cn.js";
|
|
4
|
+
const e = {
|
|
5
|
+
default: "border-gray-v2-300 focus-within:border-brand-v2 focus-within:outline-brand-v2",
|
|
6
|
+
invalid: "border-error-v2-300 focus-within:border-error-v2 focus-within:outline-error-v2",
|
|
7
|
+
disabled: "bg-gray-v2-50 border-gray-v2-300 cursor-not-allowed"
|
|
8
|
+
}, p = u(
|
|
9
|
+
({ invalid: o, prefix: a, suffix: n, className: i, disabled: r, ...t }, l) => /* @__PURE__ */ d(
|
|
10
|
+
"span",
|
|
11
|
+
{
|
|
12
|
+
className: c(
|
|
13
|
+
"bg-white rounded-lg border shadow-xs flex items-center gap-2 px-3 min-h-10 text-md transition-all outline outline-transparent",
|
|
14
|
+
o ? e.invalid : e.default,
|
|
15
|
+
{ [e.disabled]: r },
|
|
16
|
+
i
|
|
17
|
+
),
|
|
18
|
+
children: [
|
|
19
|
+
a,
|
|
20
|
+
/* @__PURE__ */ s(
|
|
21
|
+
"input",
|
|
22
|
+
{
|
|
23
|
+
ref: l,
|
|
24
|
+
disabled: r,
|
|
25
|
+
"aria-invalid": o,
|
|
26
|
+
className: "flex-1 min-w-0 bg-transparent outline-none text-gray-v2-900 placeholder:text-gray-v2-500 disabled:text-gray-v2-400 disabled:cursor-not-allowed",
|
|
27
|
+
...t,
|
|
28
|
+
placeholder: r ? void 0 : t.placeholder
|
|
29
|
+
}
|
|
30
|
+
),
|
|
31
|
+
n
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
p.displayName = "Input";
|
|
37
|
+
export {
|
|
38
|
+
p as Input
|
|
39
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface Props {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const Modal: ({ isOpen, onOpenChange, children, className }: Props) => import('react').ReactPortal;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as r, jsxs as h } from "react/jsx-runtime";
|
|
2
|
+
import { useId as p, useRef as f, useCallback as x, useEffect as g } from "react";
|
|
3
|
+
import { createPortal as C } from "react-dom";
|
|
4
|
+
import { XClose as w } from "@untitled-ui/icons-react";
|
|
5
|
+
import { Button as E } from "../button/Button.js";
|
|
6
|
+
import { cn as u } from "../../utils/cn.js";
|
|
7
|
+
import { ModalContext as k } from "./modal-context.js";
|
|
8
|
+
import { useClickOutsideModal as A } from "./use-click-outside-modal.js";
|
|
9
|
+
const D = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])', B = ({ isOpen: e, onOpenChange: a, children: m, className: b }) => {
|
|
10
|
+
const i = p(), n = f(null), o = f(null), y = (t) => {
|
|
11
|
+
if (!(!e || o.current === null) && (t.key === "Escape" && a(!1), t.key === "Tab")) {
|
|
12
|
+
const l = Array.from(
|
|
13
|
+
o.current.querySelectorAll(D)
|
|
14
|
+
).filter((v) => !v.closest("[inert]"));
|
|
15
|
+
if (l.length === 0) return;
|
|
16
|
+
const c = l[0], d = l[l.length - 1];
|
|
17
|
+
t.shiftKey ? document.activeElement === c && (t.preventDefault(), d.focus()) : document.activeElement === d && (t.preventDefault(), c.focus());
|
|
18
|
+
}
|
|
19
|
+
}, s = x(
|
|
20
|
+
() => a(!1),
|
|
21
|
+
[a]
|
|
22
|
+
);
|
|
23
|
+
return A(s, n, [o]), g(() => (document.body.classList.toggle("overflow-hidden", e), () => document.body.classList.remove("overflow-hidden")), [e]), C(
|
|
24
|
+
/* @__PURE__ */ r(k.Provider, { value: i, children: /* @__PURE__ */ r(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
ref: n,
|
|
28
|
+
className: u(
|
|
29
|
+
"fixed inset-0 flex items-center justify-center z-10 transition-opacity bg-gray-v2-950/48",
|
|
30
|
+
{ "opacity-0 pointer-events-none": !e }
|
|
31
|
+
),
|
|
32
|
+
onKeyDown: y,
|
|
33
|
+
children: /* @__PURE__ */ h(
|
|
34
|
+
"div",
|
|
35
|
+
{
|
|
36
|
+
ref: o,
|
|
37
|
+
role: "dialog",
|
|
38
|
+
"aria-modal": "true",
|
|
39
|
+
"aria-labelledby": i,
|
|
40
|
+
className: u(
|
|
41
|
+
"relative min-h-20 w-xl p-6 rounded-xl bg-white flex flex-col gap-5 overflow-hidden transition-all",
|
|
42
|
+
{ "scale-90": !e },
|
|
43
|
+
b
|
|
44
|
+
),
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ r(
|
|
47
|
+
E,
|
|
48
|
+
{
|
|
49
|
+
variant: "tertiary",
|
|
50
|
+
color: "gray",
|
|
51
|
+
className: "absolute right-0 top-0 m-4",
|
|
52
|
+
onClick: s,
|
|
53
|
+
"aria-label": "Close",
|
|
54
|
+
children: /* @__PURE__ */ r(w, { className: "size-6 shrink-0 text-gray-v2-400" })
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
m
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
) }),
|
|
63
|
+
document.body
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
export {
|
|
67
|
+
B as Modal
|
|
68
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode, SVGProps } from 'react';
|
|
2
|
+
type IconType = "brand" | "gray" | "success" | "warning" | "error";
|
|
3
|
+
interface Props {
|
|
4
|
+
type?: IconType;
|
|
5
|
+
Icon: (props: SVGProps<SVGSVGElement>) => ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare const ModalHeaderIcon: ({ type, Icon }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsxs as o, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { cn as n } from "../../../utils/cn.js";
|
|
4
|
+
import a from "./circles-sm.svg.js";
|
|
5
|
+
const t = {
|
|
6
|
+
brand: "bg-brand-v2-100 border-brand-v2-50",
|
|
7
|
+
gray: "bg-gray-v2-100 border-gray-v2-50",
|
|
8
|
+
success: "bg-success-v2-100 border-success-v2-50",
|
|
9
|
+
warning: "bg-warning-v2-100 border-warning-v2-50",
|
|
10
|
+
error: "bg-error-v2-100 border-error-v2-50"
|
|
11
|
+
}, c = {
|
|
12
|
+
brand: "text-brand-v2-600",
|
|
13
|
+
gray: "text-gray-v2-600",
|
|
14
|
+
success: "text-success-v2-600",
|
|
15
|
+
warning: "text-warning-v2-600",
|
|
16
|
+
error: "text-error-v2-600"
|
|
17
|
+
}, g = ({ type: r = "brand", Icon: s }) => /* @__PURE__ */ o(
|
|
18
|
+
"div",
|
|
19
|
+
{
|
|
20
|
+
className: n(
|
|
21
|
+
"relative h-13 w-13 min-w-13 flex items-center justify-center rounded-full border-8",
|
|
22
|
+
t[r]
|
|
23
|
+
),
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ e(s, { className: n("h-6 w-6 min-w-6", c[r]) }),
|
|
26
|
+
/* @__PURE__ */ e(a, { className: "absolute h-84 w-84 min-w-84 pointer-events-none" })
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
export {
|
|
31
|
+
g as ModalHeaderIcon
|
|
32
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useModalContext as r } from "../modal-context.js";
|
|
3
|
+
const i = ({ children: t }) => {
|
|
4
|
+
const o = r();
|
|
5
|
+
return /* @__PURE__ */ e("h2", { id: o, className: "text-lg font-semibold text-gray-v2-900", children: t });
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
i as ModalTitle
|
|
9
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as e from "react";
|
|
2
|
+
const r = (t) => /* @__PURE__ */ e.createElement("svg", { height: 336, width: 336, viewBox: "0 0 336 336", xmlns: "http://www.w3.org/2000/svg", ...t }, /* @__PURE__ */ e.createElement("defs", null, /* @__PURE__ */ e.createElement("radialGradient", { id: "radialGradient-mask", cx: "50%", cy: "50%", r: "50%", fx: "50%", fy: "50%" }, /* @__PURE__ */ e.createElement("stop", { offset: "0%", stopColor: "white" }), /* @__PURE__ */ e.createElement("stop", { offset: "100%", stopColor: "black" })), /* @__PURE__ */ e.createElement("mask", { id: "background-pattern-decorative-mask" }, /* @__PURE__ */ e.createElement("rect", { x: 0, y: 0, height: "100%", width: "100%", fill: "url(#radialGradient-mask)" }))), /* @__PURE__ */ e.createElement("g", { mask: "url(#background-pattern-decorative-mask)" }, /* @__PURE__ */ e.createElement("circle", { cx: 168, cy: 168, r: 168, stroke: "#E9EAEB", fill: "transparent" }), /* @__PURE__ */ e.createElement("circle", { cx: 168, cy: 168, r: 144, stroke: "#E9EAEB", fill: "transparent" }), /* @__PURE__ */ e.createElement("circle", { cx: 168, cy: 168, r: 120, stroke: "#E9EAEB", fill: "transparent" }), /* @__PURE__ */ e.createElement("circle", { cx: 168, cy: 168, r: 96, stroke: "#E9EAEB", fill: "transparent" }), /* @__PURE__ */ e.createElement("circle", { cx: 168, cy: 168, r: 72, stroke: "#E9EAEB", fill: "transparent" }), /* @__PURE__ */ e.createElement("circle", { cx: 168, cy: 168, r: 48, stroke: "#E9EAEB", fill: "transparent" })));
|
|
3
|
+
export {
|
|
4
|
+
r as default
|
|
5
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEffect as u } from "react";
|
|
2
|
+
const d = (e, n, r) => {
|
|
3
|
+
u(() => {
|
|
4
|
+
function i(o) {
|
|
5
|
+
const { target: t } = o;
|
|
6
|
+
t instanceof Node && n.current && n.current.contains(t) && r.every((c) => !c.current || !c.current.contains(t)) && e();
|
|
7
|
+
}
|
|
8
|
+
return window.addEventListener("mousedown", i), () => {
|
|
9
|
+
window.removeEventListener("mousedown", i);
|
|
10
|
+
};
|
|
11
|
+
}, [n, r, e]);
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
d as useClickOutsideModal
|
|
15
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InternalToastProps } from './types';
|
|
2
|
+
import { Observable } from '../../utils/observable';
|
|
3
|
+
interface Props {
|
|
4
|
+
toast: InternalToastProps;
|
|
5
|
+
observable: Observable<InternalToastProps>;
|
|
6
|
+
}
|
|
7
|
+
/** A slice of toast. Freshly out of the toaster, crunchy on the outside, soft and tender on the inside.
|
|
8
|
+
* Or is it just notification's UI.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ToastSlice: ({ toast, observable }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsxs as c, jsx as i } from "react/jsx-runtime";
|
|
2
|
+
import { useState as f, useRef as y, useMemo as T, useCallback as p, useEffect as l } from "react";
|
|
3
|
+
import { XClose as x } from "@untitled-ui/icons-react";
|
|
4
|
+
import { Button as w } from "../button/Button.js";
|
|
5
|
+
import { iconByToastType as B, styleToastType as N } from "./icons.js";
|
|
6
|
+
import { cn as m } from "../../utils/cn.js";
|
|
7
|
+
const k = 4e3, W = ({ toast: e, observable: r }) => {
|
|
8
|
+
var a, d;
|
|
9
|
+
const [h, g] = f(!1), n = y(null), o = T(() => ["peach"].includes(e.type) ? "gradient" : "normal", [e.type]), s = p(
|
|
10
|
+
(t) => {
|
|
11
|
+
r.notify({ ...t, status: "dismissed" });
|
|
12
|
+
},
|
|
13
|
+
[r]
|
|
14
|
+
), u = p(
|
|
15
|
+
(t) => {
|
|
16
|
+
r.notify({ ...t, status: "destroyed" });
|
|
17
|
+
},
|
|
18
|
+
[r]
|
|
19
|
+
);
|
|
20
|
+
return l(() => {
|
|
21
|
+
g(!0);
|
|
22
|
+
}, []), l(() => {
|
|
23
|
+
const t = setTimeout(() => {
|
|
24
|
+
s(e);
|
|
25
|
+
}, k);
|
|
26
|
+
return () => clearTimeout(t);
|
|
27
|
+
}, [e, s]), /* @__PURE__ */ c(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
ref: n,
|
|
31
|
+
style: h ? e.status === "dismissed" ? (
|
|
32
|
+
// The last state before toast is destroyed
|
|
33
|
+
{
|
|
34
|
+
height: 0,
|
|
35
|
+
minHeight: 0,
|
|
36
|
+
opacity: 0,
|
|
37
|
+
marginBottom: 0,
|
|
38
|
+
paddingTop: 0,
|
|
39
|
+
paddingBottom: 0,
|
|
40
|
+
borderWidth: 0
|
|
41
|
+
}
|
|
42
|
+
) : (
|
|
43
|
+
// The stable state
|
|
44
|
+
{
|
|
45
|
+
height: (d = n.current) == null ? void 0 : d.offsetHeight,
|
|
46
|
+
marginBottom: "0.75rem",
|
|
47
|
+
paddingTop: "0.75rem",
|
|
48
|
+
paddingBottom: "0.75rem",
|
|
49
|
+
borderWidth: "1px"
|
|
50
|
+
}
|
|
51
|
+
) : (
|
|
52
|
+
// The first state: before toast is mounted
|
|
53
|
+
{
|
|
54
|
+
transform: "translate(0, -100%)",
|
|
55
|
+
height: (a = n.current) == null ? void 0 : a.offsetHeight,
|
|
56
|
+
marginBottom: "0.75rem",
|
|
57
|
+
paddingTop: "0.75rem",
|
|
58
|
+
paddingBottom: "0.75rem",
|
|
59
|
+
borderWidth: "1px"
|
|
60
|
+
}
|
|
61
|
+
),
|
|
62
|
+
className: m(
|
|
63
|
+
"px-6 flex items-center gap-3 rounded-md duration-300 ease-out shadow-sm-01 min-h-16 overflow-hidden",
|
|
64
|
+
N[e.type],
|
|
65
|
+
o === "gradient" && "text-white"
|
|
66
|
+
),
|
|
67
|
+
onTransitionEnd: (t) => {
|
|
68
|
+
t.target === t.currentTarget && t.propertyName === "height" && u(e);
|
|
69
|
+
},
|
|
70
|
+
children: [
|
|
71
|
+
B[e.type],
|
|
72
|
+
/* @__PURE__ */ c("div", { className: "flex flex-col", children: [
|
|
73
|
+
/* @__PURE__ */ i("span", { className: "text-md font-medium w-80 min-h-[1em] wrap-break-words", children: e.message }),
|
|
74
|
+
e.detail ? /* @__PURE__ */ i(
|
|
75
|
+
"span",
|
|
76
|
+
{
|
|
77
|
+
className: m(
|
|
78
|
+
"text-sm w-80 min-h-[1em] wrap-break-words line-clamp-3",
|
|
79
|
+
o === "gradient" ? "text-white" : "text-gray-v2-500"
|
|
80
|
+
),
|
|
81
|
+
children: e.detail
|
|
82
|
+
}
|
|
83
|
+
) : null
|
|
84
|
+
] }),
|
|
85
|
+
/* @__PURE__ */ i(
|
|
86
|
+
w,
|
|
87
|
+
{
|
|
88
|
+
variant: "tertiary",
|
|
89
|
+
color: "gray",
|
|
90
|
+
onClick: () => s(e),
|
|
91
|
+
className: m(
|
|
92
|
+
o === "gradient" && "text-white hover:bg-transparent"
|
|
93
|
+
),
|
|
94
|
+
children: /* @__PURE__ */ i(x, { className: "size-5 shrink-0 text-gray-v2-400" })
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
export {
|
|
102
|
+
W as ToastSlice
|
|
103
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { InfoCircle as r, XCircle as s, CheckCircle as i } from "@untitled-ui/icons-react";
|
|
3
|
+
const a = {
|
|
4
|
+
success: "bg-white border-success-v2-500",
|
|
5
|
+
error: "bg-white border-error-v2-500",
|
|
6
|
+
warning: "bg-white border-warning-v2-500",
|
|
7
|
+
info: "bg-white border-blue-v2-500",
|
|
8
|
+
peach: "bg-gradient-to-br from-[#d74874] to-[#da805f] border-transparent"
|
|
9
|
+
}, c = {
|
|
10
|
+
success: /* @__PURE__ */ e(i, { className: "text-success-v2-500 size-6 shrink-0" }),
|
|
11
|
+
error: /* @__PURE__ */ e(s, { className: "text-error-v2-500 size-6 shrink-0" }),
|
|
12
|
+
warning: /* @__PURE__ */ e(r, { className: "text-warning-v2-500 size-6 shrink-0" }),
|
|
13
|
+
info: /* @__PURE__ */ e(r, { className: "text-blue-v2-500 size-6 shrink-0" }),
|
|
14
|
+
peach: /* @__PURE__ */ e(r, { className: "text-white size-6 shrink-0" })
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
c as iconByToastType,
|
|
18
|
+
a as styleToastType
|
|
19
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { WrittenOnTheToast } from './types';
|
|
2
|
+
/** Where we store the newly cooked toasts. When a toast is created, it will appear here. */
|
|
3
|
+
export declare const Toaster: () => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare const createToast: (newToast: WrittenOnTheToast) => void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect as a } from "react";
|
|
3
|
+
import { ToastSlice as n } from "./ToastSlice.js";
|
|
4
|
+
import { useToastList as i } from "./use-toast-list.js";
|
|
5
|
+
import { Observable as c } from "../../utils/observable.js";
|
|
6
|
+
const l = () => window.isSecureContext ? crypto.randomUUID() : crypto.getRandomValues(new Uint32Array(1))[0].toString(), e = new c(), s = /* @__PURE__ */ new Set(), b = () => {
|
|
7
|
+
const o = i(e);
|
|
8
|
+
return a(() => {
|
|
9
|
+
const t = Symbol();
|
|
10
|
+
return s.add(t), () => {
|
|
11
|
+
s.delete(t);
|
|
12
|
+
};
|
|
13
|
+
}, []), /* @__PURE__ */ r("div", { className: "flex flex-col", children: o.map((t) => /* @__PURE__ */ r(
|
|
14
|
+
n,
|
|
15
|
+
{
|
|
16
|
+
toast: t,
|
|
17
|
+
observable: e
|
|
18
|
+
},
|
|
19
|
+
`toast-${t.id}`
|
|
20
|
+
)) });
|
|
21
|
+
}, T = (o) => {
|
|
22
|
+
process.env.NODE_ENV !== "production" && s.size === 0 && console.warn(
|
|
23
|
+
"[Toaster] createToast was called but no <Toaster> is mounted. This toast will be lost."
|
|
24
|
+
);
|
|
25
|
+
const t = {
|
|
26
|
+
...o,
|
|
27
|
+
id: l(),
|
|
28
|
+
status: "stable"
|
|
29
|
+
};
|
|
30
|
+
e.notify(t);
|
|
31
|
+
};
|
|
32
|
+
export {
|
|
33
|
+
b as Toaster,
|
|
34
|
+
T as createToast
|
|
35
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export type ToastType = "error" | "warning" | "success" | "info" | "peach";
|
|
3
|
+
type ToastStatus = "stable" | "dismissed" | "destroyed";
|
|
4
|
+
/** The name is Bon, Jambon. */
|
|
5
|
+
export interface WrittenOnTheToast {
|
|
6
|
+
type: ToastType;
|
|
7
|
+
message: string;
|
|
8
|
+
detail?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export interface InternalToastProps extends WrittenOnTheToast {
|
|
11
|
+
id: string;
|
|
12
|
+
status: ToastStatus;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useState as d, useEffect as f } from "react";
|
|
2
|
+
import { flushSync as m } from "react-dom";
|
|
3
|
+
const c = (i) => {
|
|
4
|
+
const [u, r] = d([]);
|
|
5
|
+
return f(() => i.subscribe((s) => {
|
|
6
|
+
if (s.status === "dismissed") {
|
|
7
|
+
r(
|
|
8
|
+
(t) => t.map(
|
|
9
|
+
(e) => e.id === s.id ? { ...e, status: "dismissed" } : e
|
|
10
|
+
)
|
|
11
|
+
);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (s.status === "destroyed") {
|
|
15
|
+
r((t) => t.filter((e) => e.id !== s.id));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
m(() => {
|
|
20
|
+
r((t) => [...t, s]);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}), [i]), u;
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
c as useToastList
|
|
27
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export { Button } from './components/button/Button';
|
|
2
|
+
export type { ButtonProps } from './components/button/Button';
|
|
3
|
+
export type { ButtonVariant, ButtonColor, ButtonSize, ButtonLoadingStyle, } from './components/button/button-variants';
|
|
1
4
|
export { Badge } from './components/badge/Badge';
|
|
2
5
|
export { BadgeTag, getBadgeAutoColor, TAG_ICON_COLOR_CLASSES, } from './components/badge/variants/BadgeTag';
|
|
3
6
|
export { BadgeMore } from './components/badge/variants/BadgeMore';
|
|
@@ -18,6 +21,17 @@ export { PageTitle } from './components/page-title/PageTitle';
|
|
|
18
21
|
export { Popover } from './components/popover/Popover';
|
|
19
22
|
export { usePopoverCoord } from './components/popover/use-popover-coord';
|
|
20
23
|
export type { PopoverPosition } from './components/popover/use-popover-coord';
|
|
24
|
+
export { Checkbox } from './components/checkbox/Checkbox';
|
|
21
25
|
export { Switch } from './components/switch/Switch';
|
|
22
26
|
export { Tooltip } from './components/tooltip/Tooltip';
|
|
27
|
+
export { Collapsible } from './components/collapsible/Collapsible';
|
|
28
|
+
export { Card } from './components/card/Card';
|
|
29
|
+
export type { CardSize } from './components/card/types';
|
|
30
|
+
export { Input } from './components/input/Input';
|
|
31
|
+
export { Toaster, createToast } from './components/toast/index';
|
|
32
|
+
export type { WrittenOnTheToast, ToastType } from './components/toast/types';
|
|
33
|
+
export { Modal } from './components/modal/Modal';
|
|
34
|
+
export { ModalHeaderIcon } from './components/modal/helpers/HeaderIcon';
|
|
35
|
+
export { ModalTitle } from './components/modal/helpers/Title';
|
|
36
|
+
export { useClickOutsideModal } from './components/modal/use-click-outside-modal';
|
|
23
37
|
export { cn } from './utils/cn';
|