@arcote.tech/arc-ds 0.4.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.
Files changed (49) hide show
  1. package/package.json +42 -0
  2. package/src/ds/avatar/avatar.tsx +86 -0
  3. package/src/ds/badge/badge.tsx +61 -0
  4. package/src/ds/bento-card/bento-card.tsx +70 -0
  5. package/src/ds/bento-grid/bento-grid.tsx +52 -0
  6. package/src/ds/box/box.tsx +96 -0
  7. package/src/ds/button/button.tsx +191 -0
  8. package/src/ds/card-modal/card-modal.tsx +161 -0
  9. package/src/ds/display-mode.tsx +32 -0
  10. package/src/ds/ds-provider.tsx +85 -0
  11. package/src/ds/form/field.tsx +124 -0
  12. package/src/ds/form/fields/checkbox-select-field.tsx +326 -0
  13. package/src/ds/form/fields/index.ts +14 -0
  14. package/src/ds/form/fields/search-select-field.tsx +41 -0
  15. package/src/ds/form/fields/select-field.tsx +42 -0
  16. package/src/ds/form/fields/suggestion-list-field.tsx +43 -0
  17. package/src/ds/form/fields/tag-field.tsx +39 -0
  18. package/src/ds/form/fields/text-field.tsx +35 -0
  19. package/src/ds/form/fields/textarea-field.tsx +81 -0
  20. package/src/ds/form/form-part.tsx +79 -0
  21. package/src/ds/form/form.tsx +299 -0
  22. package/src/ds/form/index.tsx +5 -0
  23. package/src/ds/form/message.tsx +14 -0
  24. package/src/ds/input/input.tsx +115 -0
  25. package/src/ds/merge-variants.ts +26 -0
  26. package/src/ds/search-select/search-select.tsx +291 -0
  27. package/src/ds/separator/separator.tsx +26 -0
  28. package/src/ds/suggestion-list/suggestion-list.tsx +406 -0
  29. package/src/ds/tag-list/tag-list.tsx +87 -0
  30. package/src/ds/tooltip/tooltip.tsx +33 -0
  31. package/src/ds/transitions.ts +12 -0
  32. package/src/ds/types.ts +131 -0
  33. package/src/index.ts +115 -0
  34. package/src/layout/drag-handle.tsx +117 -0
  35. package/src/layout/dynamic-slot.tsx +95 -0
  36. package/src/layout/expandable-panel.tsx +57 -0
  37. package/src/layout/layout.tsx +323 -0
  38. package/src/layout/overlay-provider.tsx +103 -0
  39. package/src/layout/overlay.tsx +33 -0
  40. package/src/layout/router.tsx +101 -0
  41. package/src/layout/scroll-nav.tsx +121 -0
  42. package/src/layout/slot-render-context.tsx +14 -0
  43. package/src/layout/sub-nav-shell.tsx +41 -0
  44. package/src/layout/toolbar-expand.tsx +70 -0
  45. package/src/layout/transitions.ts +12 -0
  46. package/src/layout/use-expandable.ts +59 -0
  47. package/src/lib/utils.ts +6 -0
  48. package/src/ui/tooltip.tsx +59 -0
  49. package/tsconfig.json +13 -0
@@ -0,0 +1,103 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ type ReactNode,
10
+ } from "react";
11
+ import { Overlay } from "./overlay";
12
+ import { useArcRoute } from "./router";
13
+
14
+ export const Z = {
15
+ BASE: 30,
16
+ OVERLAY: 40,
17
+ ELEVATED: 50,
18
+ } as const;
19
+
20
+ interface OverlayContextValue {
21
+ requestOverlay: (id: string, onClose: () => void) => void;
22
+ releaseOverlay: (id: string) => void;
23
+ isOverlayOwner: (id: string) => boolean;
24
+ isOverlayVisible: boolean;
25
+ zIndex: (id: string) => number;
26
+ }
27
+
28
+ const OverlayContext = createContext<OverlayContextValue | null>(null);
29
+
30
+ export function OverlayProvider({ children }: { children: ReactNode }) {
31
+ const [activeId, setActiveId] = useState<string | null>(null);
32
+ const [exitingId, setExitingId] = useState<string | null>(null);
33
+ const onCloseRef = useRef<(() => void) | null>(null);
34
+
35
+ const requestOverlay = useCallback((id: string, onClose: () => void) => {
36
+ setActiveId((prev) => {
37
+ if (prev === id) return prev;
38
+ return id;
39
+ });
40
+ onCloseRef.current = onClose;
41
+ }, []);
42
+
43
+ const releaseOverlay = useCallback((id: string) => {
44
+ setActiveId((prev) => {
45
+ if (prev === id) {
46
+ setExitingId(id);
47
+ onCloseRef.current = null;
48
+ return null;
49
+ }
50
+ return prev;
51
+ });
52
+ }, []);
53
+
54
+ const handleOverlayClose = useCallback(() => {
55
+ const fn = onCloseRef.current;
56
+ setActiveId((prev) => {
57
+ if (prev) setExitingId(prev);
58
+ return null;
59
+ });
60
+ onCloseRef.current = null;
61
+ fn?.();
62
+ }, []);
63
+
64
+ const handleExitComplete = useCallback(() => {
65
+ setExitingId(null);
66
+ }, []);
67
+
68
+ // Close overlay on route change
69
+ const path = useArcRoute();
70
+ useEffect(() => {
71
+ handleOverlayClose();
72
+ }, [path]);
73
+
74
+ const value = useMemo<OverlayContextValue>(() => {
75
+ const isOverlayOwner = (id: string) =>
76
+ activeId === id || (activeId === null && exitingId === id);
77
+
78
+ return {
79
+ requestOverlay,
80
+ releaseOverlay,
81
+ isOverlayOwner,
82
+ isOverlayVisible: activeId !== null,
83
+ zIndex: (id: string) => (isOverlayOwner(id) ? Z.ELEVATED : Z.BASE),
84
+ };
85
+ }, [activeId, exitingId, requestOverlay, releaseOverlay]);
86
+
87
+ return (
88
+ <OverlayContext.Provider value={value}>
89
+ {children}
90
+ <Overlay
91
+ visible={activeId !== null}
92
+ onClose={handleOverlayClose}
93
+ onExitComplete={handleExitComplete}
94
+ />
95
+ </OverlayContext.Provider>
96
+ );
97
+ }
98
+
99
+ export function useOverlay(): OverlayContextValue {
100
+ const ctx = useContext(OverlayContext);
101
+ if (!ctx) throw new Error("useOverlay must be used within OverlayProvider");
102
+ return ctx;
103
+ }
@@ -0,0 +1,33 @@
1
+ import { AnimatePresence, motion } from "framer-motion";
2
+ import { arcTransitions } from "./transitions";
3
+
4
+ interface OverlayProps {
5
+ visible: boolean;
6
+ onClose: () => void;
7
+ zIndex?: number;
8
+ onExitComplete?: () => void;
9
+ }
10
+
11
+ export function Overlay({
12
+ visible,
13
+ onClose,
14
+ zIndex = 40,
15
+ onExitComplete,
16
+ }: OverlayProps) {
17
+ return (
18
+ <AnimatePresence onExitComplete={onExitComplete}>
19
+ {visible && (
20
+ <motion.div
21
+ key="overlay"
22
+ initial={{ opacity: 0 }}
23
+ animate={{ opacity: 1 }}
24
+ exit={{ opacity: 0 }}
25
+ transition={arcTransitions.fade}
26
+ className="fixed inset-0 bg-black/20"
27
+ style={{ zIndex }}
28
+ onClick={onClose}
29
+ />
30
+ )}
31
+ </AnimatePresence>
32
+ );
33
+ }
@@ -0,0 +1,101 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useState,
8
+ type ReactNode,
9
+ } from "react";
10
+
11
+ interface ArcRouterState {
12
+ path: string;
13
+ params: Record<string, string>;
14
+ navigate: (path: string) => void;
15
+ back: () => void;
16
+ setParams: (params: Record<string, string>) => void;
17
+ }
18
+
19
+ const ArcRouterContext = createContext<ArcRouterState | null>(null);
20
+
21
+ export function ArcRouterProvider({ children }: { children: ReactNode }) {
22
+ const [path, setPath] = useState(() => window.location.pathname);
23
+ const [params, setParams] = useState<Record<string, string>>({});
24
+
25
+ const navigate = useCallback((newPath: string) => {
26
+ window.history.pushState(null, "", newPath);
27
+ setPath(new URL(newPath, window.location.origin).pathname);
28
+ }, []);
29
+
30
+ const back = useCallback(() => {
31
+ window.history.back();
32
+ }, []);
33
+
34
+ useEffect(() => {
35
+ const onPopState = () => setPath(window.location.pathname);
36
+ window.addEventListener("popstate", onPopState);
37
+ return () => window.removeEventListener("popstate", onPopState);
38
+ }, []);
39
+
40
+ return (
41
+ <ArcRouterContext.Provider value={{ path, params, navigate, back, setParams }}>
42
+ {children}
43
+ </ArcRouterContext.Provider>
44
+ );
45
+ }
46
+
47
+ export function useArcRouter(): ArcRouterState {
48
+ const ctx = useContext(ArcRouterContext);
49
+ if (!ctx)
50
+ throw new Error("useArcRouter must be used within ArcRouterProvider");
51
+ return ctx;
52
+ }
53
+
54
+ export function useArcNavigate(): (path: string) => void {
55
+ return useArcRouter().navigate;
56
+ }
57
+
58
+ export function useArcRoute(): string {
59
+ return useArcRouter().path;
60
+ }
61
+
62
+ export function useArcParams<T extends Record<string, string> = Record<string, string>>(): T {
63
+ return useArcRouter().params as T;
64
+ }
65
+
66
+ // --- Route pattern matching utilities ---
67
+
68
+ /**
69
+ * Match a URL path against a route pattern with `:param` segments.
70
+ * Returns extracted params or null if no match.
71
+ */
72
+ export function matchRoutePath(
73
+ pattern: string,
74
+ path: string,
75
+ ): Record<string, string> | null {
76
+ const patternParts = pattern.split("/").filter(Boolean);
77
+ const pathParts = path.split("/").filter(Boolean);
78
+
79
+ // Pattern with params can match paths of same length
80
+ if (patternParts.length !== pathParts.length) return null;
81
+
82
+ const params: Record<string, string> = {};
83
+
84
+ for (let i = 0; i < patternParts.length; i++) {
85
+ const pp = patternParts[i];
86
+ if (pp.startsWith(":")) {
87
+ params[pp.slice(1)] = pathParts[i];
88
+ } else if (pp !== pathParts[i]) {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ return params;
94
+ }
95
+
96
+ /**
97
+ * Check if a route pattern contains parameter segments (`:param`).
98
+ */
99
+ export function hasRouteParams(pattern: string): boolean {
100
+ return pattern.includes("/:");
101
+ }
@@ -0,0 +1,121 @@
1
+ import { Button } from "../ds/button/button";
2
+ import { cn } from "../lib/utils";
3
+ import { motion } from "framer-motion";
4
+ import { ChevronDown } from "lucide-react";
5
+ import {
6
+ Children,
7
+ cloneElement,
8
+ isValidElement,
9
+ useCallback,
10
+ useEffect,
11
+ useRef,
12
+ useState,
13
+ type ReactElement,
14
+ type ReactNode,
15
+ } from "react";
16
+ import { DragHandle } from "./drag-handle";
17
+ import { ExpandablePanel } from "./expandable-panel";
18
+ import { arcTransitions } from "./transitions";
19
+ import { useExpandable } from "./use-expandable";
20
+
21
+ interface ScrollNavProps {
22
+ children: ReactNode;
23
+ className?: string;
24
+ }
25
+
26
+ export function ScrollNav({ children, className }: ScrollNavProps) {
27
+ const scrollRef = useRef<HTMLDivElement>(null);
28
+ const [isOverflowing, setIsOverflowing] = useState(false);
29
+ const exp = useExpandable();
30
+
31
+ useEffect(() => {
32
+ const el = scrollRef.current;
33
+ if (!el) return;
34
+
35
+ const check = () => setIsOverflowing(el.scrollWidth > el.clientWidth);
36
+ check();
37
+
38
+ const ro = new ResizeObserver(check);
39
+ ro.observe(el);
40
+ return () => ro.disconnect();
41
+ }, [children]);
42
+
43
+ const scrollToCenter = useCallback((element: HTMLElement) => {
44
+ const container = scrollRef.current;
45
+ if (!container) return;
46
+
47
+ const containerRect = container.getBoundingClientRect();
48
+ const elementRect = element.getBoundingClientRect();
49
+
50
+ const elementCenter = elementRect.left + elementRect.width / 2;
51
+ const containerCenter = containerRect.left + containerRect.width / 2;
52
+ const offset = elementCenter - containerCenter;
53
+
54
+ container.scrollBy({ left: offset, behavior: "smooth" });
55
+ }, []);
56
+
57
+ const wrappedChildren = Children.map(children, (child) => {
58
+ if (!isValidElement(child)) return child;
59
+
60
+ return cloneElement(child as ReactElement<Record<string, unknown>>, {
61
+ onClick: (e: React.MouseEvent<HTMLElement>) => {
62
+ const originalOnClick = (child as ReactElement<Record<string, unknown>>)
63
+ .props.onClick as
64
+ | ((e: React.MouseEvent<HTMLElement>) => void)
65
+ | undefined;
66
+ originalOnClick?.(e);
67
+
68
+ const target = e.currentTarget;
69
+ requestAnimationFrame(() => scrollToCenter(target));
70
+ },
71
+ });
72
+ });
73
+
74
+ return (
75
+ <div className={cn("relative", className)}>
76
+ <motion.div
77
+ animate={
78
+ exp.isExpanded
79
+ ? { height: 0, opacity: 0 }
80
+ : { height: "auto", opacity: 1 }
81
+ }
82
+ initial={false}
83
+ transition={arcTransitions.snappy}
84
+ style={{ overflow: "hidden" }}
85
+ >
86
+ <div className="flex items-center gap-1">
87
+ <div
88
+ ref={scrollRef}
89
+ className="flex flex-1 items-center gap-1 overflow-x-auto scrollbar-none"
90
+ >
91
+ {wrappedChildren}
92
+ </div>
93
+ {isOverflowing && (
94
+ <Button
95
+ icon={ChevronDown}
96
+ displayMode="minimal"
97
+ size="icon-xs"
98
+ onClick={exp.open}
99
+ className="shrink-0"
100
+ />
101
+ )}
102
+ </div>
103
+ </motion.div>
104
+
105
+ <ExpandablePanel
106
+ open={exp.isExpanded}
107
+ height={exp.expandedHeight}
108
+ dragHeight={exp.dragHeight}
109
+ >
110
+ <div ref={exp.expandedRef} className="flex flex-col gap-1">
111
+ {children}
112
+ <DragHandle
113
+ contentHeight={exp.expandedHeight}
114
+ onHeightChange={exp.onDragHeight}
115
+ onRelease={exp.onDragRelease}
116
+ />
117
+ </div>
118
+ </ExpandablePanel>
119
+ </div>
120
+ );
121
+ }
@@ -0,0 +1,14 @@
1
+ import { createContext, useContext } from "react";
2
+ import type { ReactNode } from "react";
3
+
4
+ export type SlotRenderFn = (slotId: string, options?: { displayMode?: string; className?: string }) => ReactNode;
5
+
6
+ const SlotRenderContext = createContext<SlotRenderFn | null>(null);
7
+
8
+ export const SlotRenderProvider = SlotRenderContext.Provider;
9
+
10
+ export function useRenderSlot(): SlotRenderFn {
11
+ const render = useContext(SlotRenderContext);
12
+ if (!render) return () => null;
13
+ return render;
14
+ }
@@ -0,0 +1,41 @@
1
+ import { Button } from "../ds/button/button";
2
+ import { useMemo } from "react";
3
+ import type { ComponentType, ReactNode } from "react";
4
+ import { useSlotContent } from "./dynamic-slot";
5
+
6
+ export type SubNavTab = {
7
+ path: string;
8
+ icon?: ComponentType<{ className?: string }>;
9
+ label?: ReactNode;
10
+ tooltip?: ReactNode;
11
+ isActive?: boolean;
12
+ };
13
+
14
+ export type SubNavShellProps = {
15
+ children: ReactNode;
16
+ tabs: SubNavTab[];
17
+ onNavigate: (path: string) => void;
18
+ };
19
+
20
+ export function SubNavShell({ children, tabs, onNavigate }: SubNavShellProps) {
21
+ const tabButtons = useMemo(
22
+ () => (
23
+ <>
24
+ {tabs.map((tab) => (
25
+ <Button
26
+ key={tab.path}
27
+ icon={tab.icon}
28
+ label={tab.label}
29
+ isActive={tab.isActive}
30
+ onClick={() => onNavigate(tab.path)}
31
+ />
32
+ ))}
33
+ </>
34
+ ),
35
+ [tabs, onNavigate],
36
+ );
37
+
38
+ useSlotContent("sub-nav", "sub-nav-tabs", tabButtons);
39
+
40
+ return <div className="px-2">{children}</div>;
41
+ }
@@ -0,0 +1,70 @@
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useState,
7
+ type ReactNode,
8
+ } from "react";
9
+ import { useArcRoute } from "./router";
10
+
11
+ interface ToolbarExpandState {
12
+ expandedId: string | null;
13
+ expandedContent: ReactNode | null;
14
+ expand: (id: string, content: ReactNode) => void;
15
+ collapse: () => void;
16
+ toggle: (id: string, content: ReactNode) => void;
17
+ }
18
+
19
+ const ToolbarExpandContext = createContext<ToolbarExpandState | null>(null);
20
+
21
+ export function ToolbarExpandProvider({ children }: { children: ReactNode }) {
22
+ const [expandedId, setExpandedId] = useState<string | null>(null);
23
+ const [expandedContent, setExpandedContent] = useState<ReactNode | null>(
24
+ null,
25
+ );
26
+
27
+ const expand = useCallback((id: string, content: ReactNode) => {
28
+ setExpandedId(id);
29
+ setExpandedContent(content);
30
+ }, []);
31
+
32
+ const collapse = useCallback(() => {
33
+ setExpandedId(null);
34
+ setExpandedContent(null);
35
+ }, []);
36
+
37
+ const toggle = useCallback(
38
+ (id: string, content: ReactNode) => {
39
+ if (expandedId === id) {
40
+ collapse();
41
+ } else {
42
+ expand(id, content);
43
+ }
44
+ },
45
+ [expandedId, collapse, expand],
46
+ );
47
+
48
+ // Close expanded toolbar on route change
49
+ const path = useArcRoute();
50
+ useEffect(() => {
51
+ collapse();
52
+ }, [path]);
53
+
54
+ return (
55
+ <ToolbarExpandContext.Provider
56
+ value={{ expandedId, expandedContent, expand, collapse, toggle }}
57
+ >
58
+ {children}
59
+ </ToolbarExpandContext.Provider>
60
+ );
61
+ }
62
+
63
+ export function useToolbarExpand(): ToolbarExpandState {
64
+ const ctx = useContext(ToolbarExpandContext);
65
+ if (!ctx)
66
+ throw new Error(
67
+ "useToolbarExpand must be used within ToolbarExpandProvider",
68
+ );
69
+ return ctx;
70
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Arc layout transition presets.
3
+ * Jedno miejsce do tuningu animacji — wszystkie layout komponenty importują stąd.
4
+ */
5
+ export const arcTransitions = {
6
+ /** Szybki spring — expand/collapse paneli, nav overflow. */
7
+ snappy: { type: "spring" as const, stiffness: 500, damping: 35 },
8
+ /** Łagodniejszy spring — layout morphing, ArcBox. */
9
+ smooth: { type: "spring" as const, stiffness: 400, damping: 30 },
10
+ /** Prosty fade — overlay. */
11
+ fade: { duration: 0.15 },
12
+ } as const;
@@ -0,0 +1,59 @@
1
+ import { useCallback, useLayoutEffect, useRef, useState } from "react";
2
+
3
+ export interface UseExpandableReturn {
4
+ isExpanded: boolean;
5
+ open: () => void;
6
+ close: () => void;
7
+ toggle: () => void;
8
+ expandedRef: React.RefObject<HTMLDivElement | null>;
9
+ expandedHeight: number;
10
+ dragHeight: number | null;
11
+ onDragHeight: (height: number) => void;
12
+ onDragRelease: (shouldClose: boolean) => void;
13
+ isDragging: boolean;
14
+ }
15
+
16
+ export function useExpandable(externalOpen?: boolean): UseExpandableReturn {
17
+ const [internalExpanded, setInternalExpanded] = useState(false);
18
+ const expandedRef = useRef<HTMLDivElement>(null);
19
+ const [expandedHeight, setExpandedHeight] = useState(0);
20
+ const [dragHeight, setDragHeight] = useState<number | null>(null);
21
+
22
+ const isExpanded = externalOpen ?? internalExpanded;
23
+
24
+ const open = useCallback(() => setInternalExpanded(true), []);
25
+ const close = useCallback(() => setInternalExpanded(false), []);
26
+ const toggle = useCallback(() => setInternalExpanded((v) => !v), []);
27
+
28
+ useLayoutEffect(() => {
29
+ const el = expandedRef.current;
30
+ if (!el) return;
31
+
32
+ const measure = () => setExpandedHeight(el.scrollHeight);
33
+ measure();
34
+
35
+ const ro = new ResizeObserver(measure);
36
+ ro.observe(el);
37
+ return () => ro.disconnect();
38
+ });
39
+
40
+ const onDragHeight = useCallback((h: number) => setDragHeight(h), []);
41
+
42
+ const onDragRelease = useCallback((shouldClose: boolean) => {
43
+ setDragHeight(null);
44
+ if (shouldClose) setInternalExpanded(false);
45
+ }, []);
46
+
47
+ return {
48
+ isExpanded,
49
+ open,
50
+ close,
51
+ toggle,
52
+ expandedRef,
53
+ expandedHeight,
54
+ dragHeight,
55
+ onDragHeight,
56
+ onDragRelease,
57
+ isDragging: dragHeight !== null,
58
+ };
59
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,59 @@
1
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/utils";
4
+
5
+ function TooltipProvider({
6
+ delayDuration = 0,
7
+ ...props
8
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
9
+ return (
10
+ <TooltipPrimitive.Provider
11
+ data-slot="tooltip-provider"
12
+ delayDuration={delayDuration}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function TooltipRoot({
19
+ ...props
20
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
21
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
22
+ }
23
+
24
+ function TooltipTrigger({
25
+ ...props
26
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
27
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
28
+ }
29
+
30
+ function TooltipContent({
31
+ className,
32
+ sideOffset = 0,
33
+ children,
34
+ ...props
35
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
36
+ return (
37
+ <TooltipPrimitive.Portal>
38
+ <TooltipPrimitive.Content
39
+ data-slot="tooltip-content"
40
+ sideOffset={sideOffset}
41
+ className={cn(
42
+ "z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
43
+ className,
44
+ )}
45
+ {...props}
46
+ >
47
+ {children}
48
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
49
+ </TooltipPrimitive.Content>
50
+ </TooltipPrimitive.Portal>
51
+ );
52
+ }
53
+
54
+ export {
55
+ TooltipRoot as Tooltip,
56
+ TooltipContent,
57
+ TooltipProvider,
58
+ TooltipTrigger,
59
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "jsx": "react-jsx",
6
+ "baseUrl": ".",
7
+ "paths": {
8
+ "@/*": ["./src/*"]
9
+ }
10
+ },
11
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
12
+ "exclude": ["node_modules", "dist"]
13
+ }