@particle-academy/react-fancy 5.63.0 → 5.64.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/CHANGELOG.md +61 -0
- package/README.md +1 -0
- package/dist/chunk-BXPLERIJ.js +293 -0
- package/dist/chunk-BXPLERIJ.js.map +1 -0
- package/dist/chunk-DTOIO42M.js +605 -0
- package/dist/chunk-DTOIO42M.js.map +1 -0
- package/dist/chunk-QP6VNKLW.cjs +608 -0
- package/dist/chunk-QP6VNKLW.cjs.map +1 -0
- package/dist/chunk-XZOBGKZL.cjs +296 -0
- package/dist/chunk-XZOBGKZL.cjs.map +1 -0
- package/dist/dropdown.cjs +3 -3
- package/dist/dropdown.d.cts +10 -0
- package/dist/dropdown.d.ts +10 -0
- package/dist/dropdown.js +1 -1
- package/dist/index.cjs +30 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +10 -10
- package/dist/inputs.js +2 -2
- package/dist/reorderable-grid.cjs +20 -0
- package/dist/reorderable-grid.cjs.map +1 -0
- package/dist/reorderable-grid.d.cts +141 -0
- package/dist/reorderable-grid.d.ts +141 -0
- package/dist/reorderable-grid.js +7 -0
- package/dist/reorderable-grid.js.map +1 -0
- package/docs/ReorderableGrid.md +233 -0
- package/package.json +11 -1
- package/dist/chunk-A6HWKQ4D.cjs +0 -172
- package/dist/chunk-A6HWKQ4D.cjs.map +0 -1
- package/dist/chunk-JG2DJK5A.js +0 -169
- package/dist/chunk-JG2DJK5A.js.map +0 -1
package/dist/chunk-JG2DJK5A.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { useAnimation } from './chunk-3K2CEAI7.js';
|
|
2
|
-
import { useFloatingPosition } from './chunk-MQ6VGG62.js';
|
|
3
|
-
import { Portal } from './chunk-RBCMKPBX.js';
|
|
4
|
-
import { useOutsideClick } from './chunk-RGANGU7X.js';
|
|
5
|
-
import { useEscapeKey } from './chunk-YX6FTJ6N.js';
|
|
6
|
-
import { cn } from './chunk-MMW2JGKZ.js';
|
|
7
|
-
import { createContext, useState, useRef, useMemo, useCallback, useEffect, useContext } from 'react';
|
|
8
|
-
import { jsx } from 'react/jsx-runtime';
|
|
9
|
-
|
|
10
|
-
var DropdownContext = createContext(null);
|
|
11
|
-
function useDropdown() {
|
|
12
|
-
const ctx = useContext(DropdownContext);
|
|
13
|
-
if (!ctx) {
|
|
14
|
-
throw new Error(
|
|
15
|
-
"Dropdown compound components must be used within <Dropdown>"
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
return ctx;
|
|
19
|
-
}
|
|
20
|
-
function DropdownTrigger({ children }) {
|
|
21
|
-
const { setOpen, open, anchorRef } = useDropdown();
|
|
22
|
-
return /* @__PURE__ */ jsx(
|
|
23
|
-
"span",
|
|
24
|
-
{
|
|
25
|
-
ref: anchorRef,
|
|
26
|
-
"data-react-fancy-dropdown-trigger": "",
|
|
27
|
-
className: "inline-flex",
|
|
28
|
-
onClick: () => setOpen(!open),
|
|
29
|
-
"aria-expanded": open,
|
|
30
|
-
"aria-haspopup": "menu",
|
|
31
|
-
children
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
DropdownTrigger.displayName = "DropdownTrigger";
|
|
36
|
-
function DropdownItems({ children, className }) {
|
|
37
|
-
const { open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset } = useDropdown();
|
|
38
|
-
const floatingRef = useRef(null);
|
|
39
|
-
const outsideRef = useRef(null);
|
|
40
|
-
const position = useFloatingPosition(anchorRef, floatingRef, {
|
|
41
|
-
placement,
|
|
42
|
-
offset,
|
|
43
|
-
enabled: open
|
|
44
|
-
});
|
|
45
|
-
const close = useCallback(() => {
|
|
46
|
-
setOpen(false);
|
|
47
|
-
setActiveIndex(-1);
|
|
48
|
-
}, [setOpen, setActiveIndex]);
|
|
49
|
-
useOutsideClick(outsideRef, close, open, anchorRef);
|
|
50
|
-
useEscapeKey(close, open);
|
|
51
|
-
const { mounted, className: animClass, ref: animRef } = useAnimation({
|
|
52
|
-
open,
|
|
53
|
-
enterClass: "fancy-scale-in",
|
|
54
|
-
exitClass: "fancy-fade-out"
|
|
55
|
-
});
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (!open || !floatingRef.current) return;
|
|
58
|
-
const handleKeyDown = (e) => {
|
|
59
|
-
const items = floatingRef.current?.querySelectorAll(
|
|
60
|
-
'[role="menuitem"]:not([aria-disabled="true"])'
|
|
61
|
-
);
|
|
62
|
-
if (!items?.length) return;
|
|
63
|
-
if (e.key === "ArrowDown") {
|
|
64
|
-
e.preventDefault();
|
|
65
|
-
const next = activeIndex < items.length - 1 ? activeIndex + 1 : 0;
|
|
66
|
-
setActiveIndex(next);
|
|
67
|
-
items[next]?.focus();
|
|
68
|
-
} else if (e.key === "ArrowUp") {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
const prev = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
|
|
71
|
-
setActiveIndex(prev);
|
|
72
|
-
items[prev]?.focus();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
76
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
77
|
-
}, [open, activeIndex, setActiveIndex]);
|
|
78
|
-
if (!mounted) return null;
|
|
79
|
-
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
80
|
-
"div",
|
|
81
|
-
{
|
|
82
|
-
ref: (node) => {
|
|
83
|
-
outsideRef.current = node;
|
|
84
|
-
floatingRef.current = node;
|
|
85
|
-
animRef.current = node;
|
|
86
|
-
},
|
|
87
|
-
"data-react-fancy-dropdown": "",
|
|
88
|
-
role: "menu",
|
|
89
|
-
className: cn(
|
|
90
|
-
"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
|
|
91
|
-
animClass,
|
|
92
|
-
className
|
|
93
|
-
),
|
|
94
|
-
style: { left: position.x, top: position.y },
|
|
95
|
-
children
|
|
96
|
-
}
|
|
97
|
-
) });
|
|
98
|
-
}
|
|
99
|
-
DropdownItems.displayName = "DropdownItems";
|
|
100
|
-
function DropdownItem({
|
|
101
|
-
children,
|
|
102
|
-
onClick,
|
|
103
|
-
disabled = false,
|
|
104
|
-
danger = false,
|
|
105
|
-
className
|
|
106
|
-
}) {
|
|
107
|
-
const { setOpen } = useDropdown();
|
|
108
|
-
return /* @__PURE__ */ jsx(
|
|
109
|
-
"button",
|
|
110
|
-
{
|
|
111
|
-
"data-react-fancy-dropdown-item": "",
|
|
112
|
-
type: "button",
|
|
113
|
-
role: "menuitem",
|
|
114
|
-
disabled,
|
|
115
|
-
"aria-disabled": disabled,
|
|
116
|
-
className: cn(
|
|
117
|
-
"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors",
|
|
118
|
-
danger ? "text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950" : "text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
|
|
119
|
-
disabled && "cursor-not-allowed opacity-50",
|
|
120
|
-
className
|
|
121
|
-
),
|
|
122
|
-
onClick: () => {
|
|
123
|
-
if (disabled) return;
|
|
124
|
-
onClick?.();
|
|
125
|
-
setOpen(false);
|
|
126
|
-
},
|
|
127
|
-
children
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
DropdownItem.displayName = "DropdownItem";
|
|
132
|
-
function DropdownSeparator({ className }) {
|
|
133
|
-
return /* @__PURE__ */ jsx(
|
|
134
|
-
"div",
|
|
135
|
-
{
|
|
136
|
-
"data-react-fancy-dropdown-separator": "",
|
|
137
|
-
role: "separator",
|
|
138
|
-
className: cn(
|
|
139
|
-
"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]",
|
|
140
|
-
className
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
DropdownSeparator.displayName = "DropdownSeparator";
|
|
146
|
-
function DropdownRoot({
|
|
147
|
-
children,
|
|
148
|
-
placement = "bottom-start",
|
|
149
|
-
offset = 4
|
|
150
|
-
}) {
|
|
151
|
-
const [open, setOpen] = useState(false);
|
|
152
|
-
const [activeIndex, setActiveIndex] = useState(-1);
|
|
153
|
-
const anchorRef = useRef(null);
|
|
154
|
-
const ctx = useMemo(
|
|
155
|
-
() => ({ open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset }),
|
|
156
|
-
[open, anchorRef, activeIndex, placement, offset]
|
|
157
|
-
);
|
|
158
|
-
return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: ctx, children });
|
|
159
|
-
}
|
|
160
|
-
var Dropdown = Object.assign(DropdownRoot, {
|
|
161
|
-
Trigger: DropdownTrigger,
|
|
162
|
-
Items: DropdownItems,
|
|
163
|
-
Item: DropdownItem,
|
|
164
|
-
Separator: DropdownSeparator
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
export { Dropdown, useDropdown };
|
|
168
|
-
//# sourceMappingURL=chunk-JG2DJK5A.js.map
|
|
169
|
-
//# sourceMappingURL=chunk-JG2DJK5A.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Dropdown/Dropdown.context.ts","../src/components/Dropdown/DropdownTrigger.tsx","../src/components/Dropdown/DropdownItems.tsx","../src/components/Dropdown/DropdownItem.tsx","../src/components/Dropdown/DropdownSeparator.tsx","../src/components/Dropdown/Dropdown.tsx"],"names":["jsx","useRef"],"mappings":"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","file":"chunk-JG2DJK5A.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DropdownContextValue } from \"./Dropdown.types\";\n\nexport const DropdownContext = createContext<DropdownContextValue | null>(null);\n\nexport function useDropdown(): DropdownContextValue {\n const ctx = useContext(DropdownContext);\n if (!ctx) {\n throw new Error(\n \"Dropdown compound components must be used within <Dropdown>\",\n );\n }\n return ctx;\n}\n","import { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownTriggerProps } from \"./Dropdown.types\";\n\nexport function DropdownTrigger({ children }: DropdownTriggerProps) {\n const { setOpen, open, anchorRef } = useDropdown();\n\n return (\n <span\n ref={anchorRef as React.RefObject<HTMLSpanElement>}\n data-react-fancy-dropdown-trigger=\"\"\n className=\"inline-flex\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n >\n {children}\n </span>\n );\n}\n\nDropdownTrigger.displayName = \"DropdownTrigger\";\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport { useFloatingPosition } from \"../../hooks/use-floating-position\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { DropdownItemsProps } from \"./Dropdown.types\";\n\nexport function DropdownItems({ children, className }: DropdownItemsProps) {\n const { open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset } =\n useDropdown();\n const floatingRef = useRef<HTMLDivElement>(null);\n const outsideRef = useRef<HTMLDivElement>(null);\n\n const position = useFloatingPosition(anchorRef, floatingRef, {\n placement,\n offset,\n enabled: open,\n });\n\n const close = useCallback(() => {\n setOpen(false);\n setActiveIndex(-1);\n }, [setOpen, setActiveIndex]);\n\n useOutsideClick(outsideRef, close, open, anchorRef);\n useEscapeKey(close, open);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-scale-in\",\n exitClass: \"fancy-fade-out\",\n });\n\n useEffect(() => {\n if (!open || !floatingRef.current) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const items = floatingRef.current?.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])',\n );\n if (!items?.length) return;\n\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n const next = activeIndex < items.length - 1 ? activeIndex + 1 : 0;\n setActiveIndex(next);\n items[next]?.focus();\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n const prev = activeIndex > 0 ? activeIndex - 1 : items.length - 1;\n setActiveIndex(prev);\n items[prev]?.focus();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, activeIndex, setActiveIndex]);\n\n if (!mounted) return null;\n\n return (\n <Portal>\n <div\n ref={(node) => {\n outsideRef.current = node;\n floatingRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n }}\n data-react-fancy-dropdown=\"\"\n role=\"menu\"\n className={cn(\n \"fixed z-50 min-w-[8rem] rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] p-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n animClass,\n className,\n )}\n style={{ left: position.x, top: position.y }}\n >\n {children}\n </div>\n </Portal>\n );\n}\n\nDropdownItems.displayName = \"DropdownItems\";\n","import { cn } from \"../../utils/cn\";\nimport { useDropdown } from \"./Dropdown.context\";\nimport type { DropdownItemProps } from \"./Dropdown.types\";\n\nexport function DropdownItem({\n children,\n onClick,\n disabled = false,\n danger = false,\n className,\n}: DropdownItemProps) {\n const { setOpen } = useDropdown();\n\n return (\n <button\n data-react-fancy-dropdown-item=\"\"\n type=\"button\"\n role=\"menuitem\"\n disabled={disabled}\n aria-disabled={disabled}\n className={cn(\n \"flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors\",\n danger\n ? \"text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950\"\n : \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n onClick={() => {\n if (disabled) return;\n onClick?.();\n setOpen(false);\n }}\n >\n {children}\n </button>\n );\n}\n\nDropdownItem.displayName = \"DropdownItem\";\n","import { cn } from \"../../utils/cn\";\nimport type { DropdownSeparatorProps } from \"./Dropdown.types\";\n\nexport function DropdownSeparator({ className }: DropdownSeparatorProps) {\n return (\n <div\n data-react-fancy-dropdown-separator=\"\"\n role=\"separator\"\n className={cn(\n \"my-1 h-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n />\n );\n}\n\nDropdownSeparator.displayName = \"DropdownSeparator\";\n","import { useMemo, useRef, useState } from \"react\";\nimport { DropdownContext } from \"./Dropdown.context\";\nimport { DropdownTrigger } from \"./DropdownTrigger\";\nimport { DropdownItems } from \"./DropdownItems\";\nimport { DropdownItem } from \"./DropdownItem\";\nimport { DropdownSeparator } from \"./DropdownSeparator\";\nimport type { DropdownProps } from \"./Dropdown.types\";\n\nfunction DropdownRoot({\n children,\n placement = \"bottom-start\",\n offset = 4,\n}: DropdownProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState(-1);\n const anchorRef = useRef<HTMLElement>(null);\n\n const ctx = useMemo(\n () => ({ open, setOpen, anchorRef, activeIndex, setActiveIndex, placement, offset }),\n [open, anchorRef, activeIndex, placement, offset],\n );\n\n return (\n <DropdownContext.Provider value={ctx}>{children}</DropdownContext.Provider>\n );\n}\n\nexport const Dropdown = Object.assign(DropdownRoot, {\n Trigger: DropdownTrigger,\n Items: DropdownItems,\n Item: DropdownItem,\n Separator: DropdownSeparator,\n});\n"]}
|