@jbpark/ui-kit 3.7.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Card.d.mts +1 -1
- package/dist/Card.d.mts.map +1 -1
- package/dist/Card.mjs.map +1 -1
- package/dist/Space.d.mts +1 -1
- package/dist/Space.d.mts.map +1 -1
- package/dist/Space.mjs.map +1 -1
- package/dist/components/atoms/checkbox/group.d.mts.map +1 -1
- package/dist/components/atoms/checkbox/group.mjs +4 -3
- package/dist/components/atoms/checkbox/group.mjs.map +1 -1
- package/dist/components/atoms/color-picker.d.mts +7 -1
- package/dist/components/atoms/color-picker.d.mts.map +1 -1
- package/dist/components/atoms/color-picker.mjs +8 -1
- package/dist/components/atoms/color-picker.mjs.map +1 -1
- package/dist/components/atoms/float-button/back-top.d.mts.map +1 -1
- package/dist/components/atoms/float-button/back-top.mjs +3 -1
- package/dist/components/atoms/float-button/back-top.mjs.map +1 -1
- package/dist/components/atoms/input/search.d.mts.map +1 -1
- package/dist/components/atoms/input/search.mjs +3 -1
- package/dist/components/atoms/input/search.mjs.map +1 -1
- package/dist/components/atoms/option-group.mjs +11 -1
- package/dist/components/atoms/option-group.mjs.map +1 -1
- package/dist/components/atoms/radio/group.d.mts.map +1 -1
- package/dist/components/atoms/radio/group.mjs +3 -2
- package/dist/components/atoms/radio/group.mjs.map +1 -1
- package/dist/components/atoms/skeleton/skeleton.d.mts.map +1 -1
- package/dist/components/atoms/skeleton/skeleton.mjs +5 -1
- package/dist/components/atoms/skeleton/skeleton.mjs.map +1 -1
- package/dist/components/atoms/spin.d.mts +2 -1
- package/dist/components/atoms/spin.d.mts.map +1 -1
- package/dist/components/atoms/spin.mjs +10 -4
- package/dist/components/atoms/spin.mjs.map +1 -1
- package/dist/components/molecules/collapse.d.mts +1 -1
- package/dist/components/molecules/collapse.d.mts.map +1 -1
- package/dist/components/molecules/collapse.mjs.map +1 -1
- package/dist/components/molecules/dropdown.d.mts +2 -2
- package/dist/components/molecules/dropdown.d.mts.map +1 -1
- package/dist/components/molecules/dropdown.mjs +25 -12
- package/dist/components/molecules/dropdown.mjs.map +1 -1
- package/dist/components/molecules/index.d.mts +9 -6
- package/dist/components/molecules/menu/item/item.mjs +27 -11
- package/dist/components/molecules/menu/item/item.mjs.map +1 -1
- package/dist/components/molecules/menu/menu.d.mts.map +1 -1
- package/dist/components/molecules/menu/menu.mjs +9 -7
- package/dist/components/molecules/menu/menu.mjs.map +1 -1
- package/dist/components/molecules/splitter/splitter.mjs +2 -2
- package/dist/components/molecules/splitter/splitter.mjs.map +1 -1
- package/dist/components/molecules/upload.d.mts +1 -1
- package/dist/components/organisms/drawer.d.mts.map +1 -1
- package/dist/components/organisms/drawer.mjs +3 -1
- package/dist/components/organisms/drawer.mjs.map +1 -1
- package/dist/components/organisms/imperative-stack.mjs +106 -0
- package/dist/components/organisms/imperative-stack.mjs.map +1 -0
- package/dist/components/organisms/index.d.mts +3 -1
- package/dist/components/organisms/list/index.d.mts +1 -1
- package/dist/components/organisms/list/index.mjs.map +1 -1
- package/dist/components/organisms/list/list.d.mts +2 -2
- package/dist/components/organisms/list/list.d.mts.map +1 -1
- package/dist/components/organisms/list/list.mjs +20 -11
- package/dist/components/organisms/list/list.mjs.map +1 -1
- package/dist/components/organisms/modal.d.mts +2 -2
- package/dist/components/organisms/modal.d.mts.map +1 -1
- package/dist/components/organisms/modal.mjs +21 -101
- package/dist/components/organisms/modal.mjs.map +1 -1
- package/dist/components/organisms/swiper/index.d.mts +1 -1
- package/dist/components/organisms/swiper/index.mjs.map +1 -1
- package/dist/components/organisms/swiper/swiper.d.mts +2 -1
- package/dist/components/organisms/swiper/swiper.d.mts.map +1 -1
- package/dist/components/organisms/swiper/swiper.mjs +5 -3
- package/dist/components/organisms/swiper/swiper.mjs.map +1 -1
- package/dist/components/organisms/toast.d.mts +1 -1
- package/dist/components/organisms/toast.d.mts.map +1 -1
- package/dist/components/organisms/toast.mjs +35 -111
- package/dist/components/organisms/toast.mjs.map +1 -1
- package/dist/components/templates/container.d.mts +19 -0
- package/dist/components/templates/container.d.mts.map +1 -0
- package/dist/components/templates/container.mjs +32 -0
- package/dist/components/templates/container.mjs.map +1 -0
- package/dist/components/templates/empty.d.mts +20 -0
- package/dist/components/templates/empty.d.mts.map +1 -0
- package/dist/components/templates/empty.mjs +34 -0
- package/dist/components/templates/empty.mjs.map +1 -0
- package/dist/components/templates/grid/col.d.mts +35 -0
- package/dist/components/templates/grid/col.d.mts.map +1 -0
- package/dist/components/templates/grid/col.mjs +55 -0
- package/dist/components/templates/grid/col.mjs.map +1 -0
- package/dist/components/templates/grid/index.d.mts +2 -0
- package/dist/components/templates/grid/row-context.mjs +13 -0
- package/dist/components/templates/grid/row-context.mjs.map +1 -0
- package/dist/components/templates/grid/row.d.mts +36 -0
- package/dist/components/templates/grid/row.d.mts.map +1 -0
- package/dist/components/templates/grid/row.mjs +54 -0
- package/dist/components/templates/grid/row.mjs.map +1 -0
- package/dist/components/templates/index.d.mts +13 -1
- package/dist/components/templates/layout/content.d.mts +4 -1
- package/dist/components/templates/layout/content.d.mts.map +1 -1
- package/dist/components/templates/layout/content.mjs +3 -2
- package/dist/components/templates/layout/content.mjs.map +1 -1
- package/dist/components/templates/layout/footer.d.mts +1 -1
- package/dist/components/templates/layout/footer.d.mts.map +1 -1
- package/dist/components/templates/layout/footer.mjs.map +1 -1
- package/dist/components/templates/layout/header.d.mts +4 -1
- package/dist/components/templates/layout/header.d.mts.map +1 -1
- package/dist/components/templates/layout/header.mjs +2 -2
- package/dist/components/templates/layout/header.mjs.map +1 -1
- package/dist/components/templates/layout/layout.d.mts +1 -1
- package/dist/components/templates/layout/layout.d.mts.map +1 -1
- package/dist/components/templates/layout/layout.mjs.map +1 -1
- package/dist/components/templates/layout/sider.d.mts +9 -2
- package/dist/components/templates/layout/sider.d.mts.map +1 -1
- package/dist/components/templates/layout/sider.mjs +40 -13
- package/dist/components/templates/layout/sider.mjs.map +1 -1
- package/dist/components/templates/page-header.d.mts +30 -0
- package/dist/components/templates/page-header.d.mts.map +1 -0
- package/dist/components/templates/page-header.mjs +45 -0
- package/dist/components/templates/page-header.mjs.map +1 -0
- package/dist/components/templates/result.d.mts +24 -0
- package/dist/components/templates/result.d.mts.map +1 -0
- package/dist/components/templates/result.mjs +46 -0
- package/dist/components/templates/result.mjs.map +1 -0
- package/dist/index.d.mts +29 -13
- package/dist/index.mjs +9 -3
- package/dist/providers/Config/context.d.mts +3 -2
- package/dist/providers/Config/context.d.mts.map +1 -1
- package/dist/providers/Config/context.mjs +13 -2
- package/dist/providers/Config/context.mjs.map +1 -1
- package/dist/providers/Config/index.d.mts +4 -2
- package/dist/providers/Config/index.d.mts.map +1 -1
- package/dist/providers/Config/index.mjs +14 -4
- package/dist/providers/Config/index.mjs.map +1 -1
- package/dist/providers/Config/types.d.mts +10 -1
- package/dist/providers/Config/types.d.mts.map +1 -1
- package/dist/providers.d.mts +4 -0
- package/dist/providers.mjs +4 -0
- package/dist/style.css +114 -3
- package/package.json +4 -3
- package/dist/providers/index.d.mts +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.d.mts","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"dropdown.d.mts","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"mappings":";;;;;KAWK,kBAAA,IAAsB,IAAA;AAAA,UAEV,KAAA,SAAc,KAAA,CAAM,wBAAA;EACnC,IAAA;EACA,OAAA;EACA,IAAA,GAAO,SAAA;EACP,YAAA,GAAe,kBAAA;AAAA;AAAA,cAGX,QAAA;EAAY,QAAA;EAAA,IAAA;EAAA,OAAA;EAAA,IAAA,EAAA,KAAA;EAAA,YAAA,EAAA,aAAA;EAAA,SAAA;EAAA,YAAA;EAAA,YAAA;EAAA,GAAA;AAAA,GAUf,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -2,23 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
import { cn } from "../../utils.mjs";
|
|
4
4
|
import Menu from "./menu/menu.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { useId, useRef } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useControllableState } from "@jbpark/use-hooks";
|
|
7
8
|
import { AnimatePresence, motion } from "motion/react";
|
|
8
9
|
|
|
9
10
|
//#region src/components/molecules/dropdown.tsx
|
|
10
11
|
const Dropdown = ({ children, menu, trigger = "hover", open: _open, onOpenChange: _onOpenChange = () => {}, className, onMouseEnter, onMouseLeave, ...props }) => {
|
|
11
|
-
const [
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const [open, onOpenChange] = useControllableState({
|
|
13
|
+
value: _open,
|
|
14
|
+
defaultValue: false,
|
|
15
|
+
onChange: _onOpenChange
|
|
16
|
+
});
|
|
14
17
|
const isClickTrigger = trigger === "click";
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
_onOpenChange(nextOpen);
|
|
18
|
-
};
|
|
18
|
+
const menuId = useId();
|
|
19
|
+
const containerRef = useRef(null);
|
|
19
20
|
return /* @__PURE__ */ jsxs("div", {
|
|
20
21
|
...props,
|
|
21
|
-
|
|
22
|
+
ref: containerRef,
|
|
23
|
+
className: cn("relative", "inline-block cursor-pointer", className),
|
|
22
24
|
onMouseEnter: (event) => {
|
|
23
25
|
onMouseEnter?.(event);
|
|
24
26
|
if (isClickTrigger) return;
|
|
@@ -30,8 +32,11 @@ const Dropdown = ({ children, menu, trigger = "hover", open: _open, onOpenChange
|
|
|
30
32
|
onOpenChange(false);
|
|
31
33
|
},
|
|
32
34
|
children: [/* @__PURE__ */ jsx("div", {
|
|
35
|
+
role: isClickTrigger ? "button" : void 0,
|
|
36
|
+
tabIndex: isClickTrigger ? 0 : void 0,
|
|
33
37
|
"aria-haspopup": "menu",
|
|
34
38
|
"aria-expanded": open,
|
|
39
|
+
"aria-controls": open ? menuId : void 0,
|
|
35
40
|
onClick: () => {
|
|
36
41
|
if (!isClickTrigger) return;
|
|
37
42
|
onOpenChange(!open);
|
|
@@ -40,11 +45,18 @@ const Dropdown = ({ children, menu, trigger = "hover", open: _open, onOpenChange
|
|
|
40
45
|
if (isClickTrigger) return;
|
|
41
46
|
onOpenChange(true);
|
|
42
47
|
},
|
|
43
|
-
onBlur: () => {
|
|
44
|
-
onOpenChange(false);
|
|
48
|
+
onBlur: (e) => {
|
|
49
|
+
if (!containerRef.current?.contains(e.relatedTarget)) onOpenChange(false);
|
|
45
50
|
},
|
|
46
51
|
onKeyDown: (e) => {
|
|
47
|
-
if (e.key === "Escape")
|
|
52
|
+
if (e.key === "Escape") {
|
|
53
|
+
onOpenChange(false);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (isClickTrigger && (e.key === "Enter" || e.key === " ")) {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
onOpenChange(!open);
|
|
59
|
+
}
|
|
48
60
|
},
|
|
49
61
|
children
|
|
50
62
|
}), /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsx(motion.div, {
|
|
@@ -64,6 +76,7 @@ const Dropdown = ({ children, menu, trigger = "hover", open: _open, onOpenChange
|
|
|
64
76
|
transition: { duration: .1 },
|
|
65
77
|
children: /* @__PURE__ */ jsx(Menu, {
|
|
66
78
|
...menu,
|
|
79
|
+
id: menuId,
|
|
67
80
|
onMouseDown: (e) => {
|
|
68
81
|
e.preventDefault();
|
|
69
82
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"sourcesContent":["'use client';\n\nimport {
|
|
1
|
+
{"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../src/components/molecules/dropdown.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useRef } from 'react';\n\nimport { useControllableState } from '@jbpark/use-hooks';\nimport { AnimatePresence, motion } from 'motion/react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Menu, { MenuProps } from './menu';\n\ntype ChangeEventHandler = (open: boolean) => void;\n\nexport interface Props extends React.ComponentPropsWithoutRef<'div'> {\n open?: boolean;\n trigger?: 'hover' | 'click';\n menu?: MenuProps;\n onOpenChange?: ChangeEventHandler;\n}\n\nconst Dropdown = ({\n children,\n menu,\n trigger = 'hover',\n open: _open,\n onOpenChange: _onOpenChange = () => {},\n className,\n onMouseEnter,\n onMouseLeave,\n ...props\n}: Props) => {\n const [open, onOpenChange] = useControllableState<boolean>({\n value: _open,\n defaultValue: false,\n onChange: _onOpenChange,\n });\n\n const isClickTrigger = trigger === 'click';\n const menuId = useId();\n const containerRef = useRef<HTMLDivElement>(null);\n\n return (\n <div\n {...props}\n ref={containerRef}\n className={cn(\n 'relative',\n 'inline-block cursor-pointer',\n className,\n //\n )}\n onMouseEnter={event => {\n onMouseEnter?.(event);\n if (isClickTrigger) {\n return;\n }\n onOpenChange(true);\n }}\n onMouseLeave={event => {\n onMouseLeave?.(event);\n if (isClickTrigger) {\n return;\n }\n onOpenChange(false);\n }}\n >\n <div\n role={isClickTrigger ? 'button' : undefined}\n tabIndex={isClickTrigger ? 0 : undefined}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? menuId : undefined}\n onClick={() => {\n if (!isClickTrigger) {\n return;\n }\n onOpenChange(!open);\n }}\n onFocus={() => {\n if (isClickTrigger) {\n return;\n }\n onOpenChange(true);\n }}\n onBlur={e => {\n // Without this check, moving focus from the trigger into the\n // menu itself (e.g. via Tab) closed the dropdown before the\n // user could ever reach it — the mouse path avoided the same\n // bug only by way of onMouseDown's preventDefault below, which\n // does nothing for keyboard focus. Mirrors the containment\n // check menu/item/item.tsx already uses for the same reason.\n if (!containerRef.current?.contains(e.relatedTarget as Node)) {\n onOpenChange(false);\n }\n }}\n onKeyDown={e => {\n if (e.key === 'Escape') {\n onOpenChange(false);\n return;\n }\n if (isClickTrigger && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onOpenChange(!open);\n }\n }}\n >\n {children}\n </div>\n <AnimatePresence>\n {open && (\n <motion.div\n className={cn(\n 'absolute top-full pt-2',\n //\n )}\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n transition={{ duration: 0.1 }}\n >\n <Menu\n {...menu}\n id={menuId}\n onMouseDown={e => {\n e.preventDefault();\n }}\n />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n};\n\nexport default Dropdown;\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,YAAY,EAChB,UACA,MACA,UAAU,SACV,MAAM,OACN,cAAc,sBAAsB,IACpC,WACA,cACA,cACA,GAAG,YACQ;CACX,MAAM,CAAC,MAAM,gBAAgB,qBAA8B;EACzD,OAAO;EACP,cAAc;EACd,UAAU;EACX,CAAC;CAEF,MAAM,iBAAiB,YAAY;CACnC,MAAM,SAAS,OAAO;CACtB,MAAM,eAAe,OAAuB,KAAK;AAEjD,QACE,qBAAC;EACC,GAAI;EACJ,KAAK;EACL,WAAW,GACT,YACA,+BACA,UAED;EACD,eAAc,UAAS;AACrB,kBAAe,MAAM;AACrB,OAAI,eACF;AAEF,gBAAa,KAAK;;EAEpB,eAAc,UAAS;AACrB,kBAAe,MAAM;AACrB,OAAI,eACF;AAEF,gBAAa,MAAM;;aAGrB,oBAAC;GACC,MAAM,iBAAiB,WAAW;GAClC,UAAU,iBAAiB,IAAI;GAC/B,iBAAc;GACd,iBAAe;GACf,iBAAe,OAAO,SAAS;GAC/B,eAAe;AACb,QAAI,CAAC,eACH;AAEF,iBAAa,CAAC,KAAK;;GAErB,eAAe;AACb,QAAI,eACF;AAEF,iBAAa,KAAK;;GAEpB,SAAQ,MAAK;AAOX,QAAI,CAAC,aAAa,SAAS,SAAS,EAAE,cAAsB,CAC1D,cAAa,MAAM;;GAGvB,YAAW,MAAK;AACd,QAAI,EAAE,QAAQ,UAAU;AACtB,kBAAa,MAAM;AACnB;;AAEF,QAAI,mBAAmB,EAAE,QAAQ,WAAW,EAAE,QAAQ,MAAM;AAC1D,OAAE,gBAAgB;AAClB,kBAAa,CAAC,KAAK;;;GAItB;IACG,EACN,oBAAC,6BACE,QACC,oBAAC,OAAO;GACN,WAAW,GACT,yBAED;GACD,SAAS;IAAE,SAAS;IAAG,GAAG;IAAK;GAC/B,SAAS;IAAE,SAAS;IAAG,GAAG;IAAG;GAC7B,MAAM;IAAE,SAAS;IAAG,GAAG;IAAK;GAC5B,YAAY,EAAE,UAAU,IAAK;aAE7B,oBAAC;IACC,GAAI;IACJ,IAAI;IACJ,cAAa,MAAK;AAChB,OAAE,gBAAgB;;KAEpB;IACS,GAEC;GACd"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import Card from "../../Card.mjs";
|
|
2
|
-
import { Menu } from "./menu/menu.mjs";
|
|
1
|
+
import Card, { Props } from "../../Card.mjs";
|
|
2
|
+
import { Menu, Props as Props$1 } from "./menu/menu.mjs";
|
|
3
3
|
import "../../Menu.mjs";
|
|
4
|
+
import { Props as Props$2 } from "./reveals/reveals.mjs";
|
|
4
5
|
import Reveals from "../../Reveals.mjs";
|
|
5
|
-
import Space from "../../Space.mjs";
|
|
6
|
-
import { Collapse } from "./collapse.mjs";
|
|
7
|
-
import { Dropdown } from "./dropdown.mjs";
|
|
6
|
+
import Space, { Props as Props$3 } from "../../Space.mjs";
|
|
7
|
+
import { Collapse, Props as Props$4 } from "./collapse.mjs";
|
|
8
|
+
import { Dropdown, Props as Props$5 } from "./dropdown.mjs";
|
|
9
|
+
import { Props as Props$6 } from "./marquees/marquees.mjs";
|
|
8
10
|
import { Marquees } from "./marquees/index.mjs";
|
|
11
|
+
import { Props as Props$7 } from "./splitter/splitter.mjs";
|
|
9
12
|
import { Splitter } from "./splitter/index.mjs";
|
|
10
|
-
import { Upload, UploadFile } from "./upload.mjs";
|
|
13
|
+
import { Props as Props$8, Upload, UploadFile } from "./upload.mjs";
|
|
@@ -22,7 +22,7 @@ const getItemClasses = (root, isHorizontal, isInline, hasChildren, customClasses
|
|
|
22
22
|
isHorizontal ? "border-b-2" : "border-r-2",
|
|
23
23
|
isInline && "border-none"
|
|
24
24
|
] : ["rounded-md", ...STYLES.hover.classes], customClasses);
|
|
25
|
-
const Item = ({ itemKey, keyPath, root = false, mode, label, children,
|
|
25
|
+
const Item = ({ itemKey, keyPath, root = false, index, mode, label, children, selectionMap, classNames, styles, offset = OFFSET, inlineOffset = INLINE_OFFSET, onClick, onSelect, ...props }) => {
|
|
26
26
|
const [open, setOpen] = useState(false);
|
|
27
27
|
const itemRef = useRef(null);
|
|
28
28
|
const isHorizontal = mode === "horizontal";
|
|
@@ -48,10 +48,30 @@ const Item = ({ itemKey, keyPath, root = false, mode, label, children, selectedK
|
|
|
48
48
|
onClick?.(params);
|
|
49
49
|
if (!selected) onSelect?.(params);
|
|
50
50
|
};
|
|
51
|
+
const handleKeyDown = (domEvent) => {
|
|
52
|
+
if (domEvent.key === "Enter" || domEvent.key === " ") {
|
|
53
|
+
domEvent.preventDefault();
|
|
54
|
+
handleSelect(domEvent);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (domEvent.key === "Escape") {
|
|
58
|
+
if (children?.length && open) {
|
|
59
|
+
domEvent.stopPropagation();
|
|
60
|
+
setOpen(false);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const isNext = isHorizontal ? domEvent.key === "ArrowRight" : domEvent.key === "ArrowDown";
|
|
65
|
+
const isPrev = isHorizontal ? domEvent.key === "ArrowLeft" : domEvent.key === "ArrowUp";
|
|
66
|
+
if (!isNext && !isPrev) return;
|
|
67
|
+
domEvent.preventDefault();
|
|
68
|
+
(isNext ? itemRef.current?.nextElementSibling : itemRef.current?.previousElementSibling)?.querySelector("[role=\"menuitem\"]")?.focus();
|
|
69
|
+
};
|
|
51
70
|
const childrenContainerClassNames = cn(isInline ? "relative overflow-hidden" : "absolute min-w-[200px]", !isInline && root && isHorizontal ? "left-0 top-full pt-2 pl-0" : !isInline ? "left-full top-0 pl-2 pt-0" : "pl-4");
|
|
52
71
|
const childrenContainerStyle = isInline ? { paddingLeft: inlineOffset } : root && isHorizontal ? { paddingTop: top } : { paddingLeft: left };
|
|
53
72
|
return /* @__PURE__ */ jsxs("li", {
|
|
54
73
|
ref: itemRef,
|
|
74
|
+
role: "none",
|
|
55
75
|
className: cn("group", "relative", root && isHorizontal ? "inline-block" : "block"),
|
|
56
76
|
onMouseEnter: () => {
|
|
57
77
|
if (isInline) return;
|
|
@@ -71,19 +91,14 @@ const Item = ({ itemKey, keyPath, root = false, mode, label, children, selectedK
|
|
|
71
91
|
},
|
|
72
92
|
children: [/* @__PURE__ */ jsx("div", {
|
|
73
93
|
role: "menuitem",
|
|
74
|
-
tabIndex: 0,
|
|
94
|
+
tabIndex: index === 0 ? 0 : -1,
|
|
75
95
|
"aria-expanded": children?.length ? open : void 0,
|
|
76
96
|
"aria-haspopup": children?.length ? "menu" : void 0,
|
|
77
|
-
"aria-
|
|
97
|
+
"aria-current": selected ? true : void 0,
|
|
78
98
|
className: cn(itemClassNames, ...selected ? root ? ["border-current"] : [...STYLES.open.classes, ...STYLES.hover.classes] : root && !!children?.length ? ["group-hover:border-current"] : []),
|
|
79
99
|
style: root ? styles?.rootItem : styles?.item,
|
|
80
100
|
onClick: handleSelect,
|
|
81
|
-
onKeyDown:
|
|
82
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
83
|
-
e.preventDefault();
|
|
84
|
-
handleSelect(e);
|
|
85
|
-
}
|
|
86
|
-
},
|
|
101
|
+
onKeyDown: handleKeyDown,
|
|
87
102
|
children: /* @__PURE__ */ jsxs("div", {
|
|
88
103
|
className: cn("w-full", "inline-flex items-center justify-between"),
|
|
89
104
|
children: [
|
|
@@ -113,16 +128,17 @@ const Item = ({ itemKey, keyPath, root = false, mode, label, children, selectedK
|
|
|
113
128
|
} : { opacity: 0 },
|
|
114
129
|
transition: { duration: .2 },
|
|
115
130
|
children: /* @__PURE__ */ jsx("ul", {
|
|
131
|
+
role: "menu",
|
|
116
132
|
className: cn(MENU_CLASSNAMES, isInline && "shadow-none", classNames?.subMenu),
|
|
117
133
|
style: styles?.subMenu,
|
|
118
|
-
children: children.map((child) => /* @__PURE__ */ createElement(Item, {
|
|
134
|
+
children: children.map((child, childIndex) => /* @__PURE__ */ createElement(Item, {
|
|
119
135
|
...child,
|
|
120
136
|
key: child.key,
|
|
121
137
|
itemKey: child.key,
|
|
122
138
|
keyPath: [...keyPath, child.key],
|
|
139
|
+
index: childIndex,
|
|
123
140
|
selectionMap,
|
|
124
141
|
mode,
|
|
125
|
-
selectedKeys,
|
|
126
142
|
classNames,
|
|
127
143
|
styles,
|
|
128
144
|
offset,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item.mjs","names":[],"sources":["../../../../../src/components/molecules/menu/item/item.tsx"],"sourcesContent":["'use client';\n\nimport { useRef, useState } from 'react';\n\nimport { ChevronDown } from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { MENU_CLASSNAMES, MenuItem, Props } from '../menu';\nimport Label from './label';\n\ninterface ItemProps extends Props, MenuItem {\n itemKey: React.Key;\n keyPath: React.Key[];\n root?: boolean;\n}\nconst OFFSET: [number, number] = [8, 8];\nconst INLINE_OFFSET = 16;\n\nconst STYLES = {\n hover: {\n classes: ['hover:underline', 'hover:underline-offset-8'],\n },\n open: {\n classes: ['underline', 'underline-offset-8'],\n },\n} as const;\n\nconst getItemClasses = (\n root: boolean,\n isHorizontal: boolean,\n isInline: boolean,\n hasChildren: boolean,\n customClasses?: string,\n) =>\n cn(\n 'px-4 py-2',\n 'cursor-pointer',\n 'whitespace-nowrap',\n root && isHorizontal ? 'inline-flex' : 'flex',\n ...(root\n ? [\n 'h-full',\n 'rounded-none border-transparent',\n hasChildren && 'hover:border-current',\n isHorizontal ? 'border-b-2' : 'border-r-2',\n isInline && 'border-none',\n ]\n : ['rounded-md', ...STYLES.hover.classes]),\n customClasses,\n );\n\nconst Item = ({\n itemKey,\n keyPath,\n root = false,\n mode,\n label,\n children,\n selectedKeys = [],\n selectionMap,\n classNames,\n styles,\n offset = OFFSET,\n inlineOffset = INLINE_OFFSET,\n onClick,\n onSelect,\n ...props\n}: ItemProps) => {\n const [open, setOpen] = useState<boolean>(false);\n\n const itemRef = useRef<HTMLLIElement | null>(null);\n\n const isHorizontal = mode === 'horizontal';\n const isInline = mode === 'inline';\n\n const [left, top] = offset;\n\n const selected = selectionMap?.get(itemKey) ?? false;\n\n const item = {\n ...props,\n label,\n key: itemKey,\n children,\n };\n\n const itemClassNames = getItemClasses(\n root,\n isHorizontal,\n isInline,\n !!children?.length,\n root ? classNames?.rootItem : classNames?.item,\n );\n\n const handleSelect = (domEvent: React.SyntheticEvent) => {\n domEvent.stopPropagation();\n\n if (isInline) {\n setOpen(!open);\n }\n\n const params = {\n domEvent,\n key: itemKey,\n keyPath,\n item,\n };\n\n onClick?.(params);\n\n if (!selected) {\n onSelect?.(params);\n }\n };\n\n const childrenContainerClassNames = cn(\n isInline ? 'relative overflow-hidden' : 'absolute min-w-[200px]',\n !isInline && root && isHorizontal\n ? 'left-0 top-full pt-2 pl-0'\n : !isInline\n ? 'left-full top-0 pl-2 pt-0'\n : 'pl-4',\n );\n\n const childrenContainerStyle = isInline\n ? {\n paddingLeft: inlineOffset,\n }\n : root && isHorizontal\n ? {\n paddingTop: top,\n }\n : {\n paddingLeft: left,\n };\n\n return (\n <li\n ref={itemRef}\n className={cn(\n 'group',\n 'relative',\n root && isHorizontal ? 'inline-block' : 'block',\n )}\n onMouseEnter={() => {\n if (isInline) {\n return;\n }\n setOpen(true);\n }}\n onMouseLeave={() => {\n if (isInline) {\n return;\n }\n setOpen(false);\n }}\n onFocus={() => {\n if (isInline) {\n return;\n }\n setOpen(true);\n }}\n onBlur={e => {\n if (isInline) {\n return;\n }\n if (!itemRef.current?.contains(e.relatedTarget as Node)) {\n setOpen(false);\n }\n }}\n >\n <div\n role=\"menuitem\"\n tabIndex={0}\n aria-expanded={children?.length ? open : undefined}\n aria-haspopup={children?.length ? 'menu' : undefined}\n aria-selected={selected}\n className={cn(\n itemClassNames,\n ...(selected\n ? root\n ? ['border-current']\n : [...STYLES.open.classes, ...STYLES.hover.classes]\n : root && !!children?.length\n ? ['group-hover:border-current']\n : []),\n //\n )}\n style={root ? styles?.rootItem : styles?.item}\n onClick={handleSelect}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleSelect(e);\n }\n }}\n >\n <div\n className={cn(\n 'w-full',\n 'inline-flex items-center justify-between',\n //\n )}\n >\n <Label className={cn(classNames?.label)} style={styles?.label}>\n {label}\n </Label>\n {!root && !isInline && !!children?.length && (\n <ChevronDown className={cn('-rotate-90')} />\n )}\n {isInline && !!children?.length && (\n <ChevronDown\n className={cn(\n 'transition-all',\n open ? '-rotate-180' : 'rotate-0',\n )}\n />\n )}\n </div>\n </div>\n <AnimatePresence>\n {!!children?.length && open && (\n <motion.div\n className={cn(\n childrenContainerClassNames,\n //\n )}\n style={childrenContainerStyle}\n initial={isInline ? { height: 0, opacity: 0 } : { opacity: 0 }}\n animate={isInline ? { height: 'auto', opacity: 1 } : { opacity: 1 }}\n exit={isInline ? { height: 0, opacity: 0 } : { opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <ul\n className={cn(\n MENU_CLASSNAMES,\n isInline && 'shadow-none',\n classNames?.subMenu,\n //\n )}\n style={styles?.subMenu}\n >\n {children.map(child => (\n <Item\n {...child}\n key={child.key}\n itemKey={child.key}\n keyPath={[...keyPath, child.key]}\n selectionMap={selectionMap}\n mode={mode}\n selectedKeys={selectedKeys}\n classNames={classNames}\n styles={styles}\n offset={offset}\n inlineOffset={inlineOffset}\n onClick={onClick}\n onSelect={onSelect}\n />\n ))}\n </ul>\n </motion.div>\n )}\n </AnimatePresence>\n </li>\n );\n};\n\nexport default Item;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"item.mjs","names":[],"sources":["../../../../../src/components/molecules/menu/item/item.tsx"],"sourcesContent":["'use client';\n\nimport { useRef, useState } from 'react';\n\nimport { ChevronDown } from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { MENU_CLASSNAMES, MenuItem, Props } from '../menu';\nimport Label from './label';\n\ninterface ItemProps extends Props, MenuItem {\n itemKey: React.Key;\n keyPath: React.Key[];\n root?: boolean;\n index: number;\n}\nconst OFFSET: [number, number] = [8, 8];\nconst INLINE_OFFSET = 16;\n\nconst STYLES = {\n hover: {\n classes: ['hover:underline', 'hover:underline-offset-8'],\n },\n open: {\n classes: ['underline', 'underline-offset-8'],\n },\n} as const;\n\nconst getItemClasses = (\n root: boolean,\n isHorizontal: boolean,\n isInline: boolean,\n hasChildren: boolean,\n customClasses?: string,\n) =>\n cn(\n 'px-4 py-2',\n 'cursor-pointer',\n 'whitespace-nowrap',\n root && isHorizontal ? 'inline-flex' : 'flex',\n ...(root\n ? [\n 'h-full',\n 'rounded-none border-transparent',\n hasChildren && 'hover:border-current',\n isHorizontal ? 'border-b-2' : 'border-r-2',\n isInline && 'border-none',\n ]\n : ['rounded-md', ...STYLES.hover.classes]),\n customClasses,\n );\n\nconst Item = ({\n itemKey,\n keyPath,\n root = false,\n index,\n mode,\n label,\n children,\n selectionMap,\n classNames,\n styles,\n offset = OFFSET,\n inlineOffset = INLINE_OFFSET,\n onClick,\n onSelect,\n ...props\n}: ItemProps) => {\n const [open, setOpen] = useState<boolean>(false);\n\n const itemRef = useRef<HTMLLIElement | null>(null);\n\n const isHorizontal = mode === 'horizontal';\n const isInline = mode === 'inline';\n\n const [left, top] = offset;\n\n const selected = selectionMap?.get(itemKey) ?? false;\n\n const item = {\n ...props,\n label,\n key: itemKey,\n children,\n };\n\n const itemClassNames = getItemClasses(\n root,\n isHorizontal,\n isInline,\n !!children?.length,\n root ? classNames?.rootItem : classNames?.item,\n );\n\n const handleSelect = (domEvent: React.SyntheticEvent) => {\n domEvent.stopPropagation();\n\n if (isInline) {\n setOpen(!open);\n }\n\n const params = {\n domEvent,\n key: itemKey,\n keyPath,\n item,\n };\n\n onClick?.(params);\n\n if (!selected) {\n onSelect?.(params);\n }\n };\n\n // Roving tabindex (WAI-ARIA menu pattern): only the first item in each\n // menu/submenu level is a Tab stop (below); arrow keys move real DOM\n // focus between sibling <li>s within that same level, which is what\n // ends up mattering for actual keyboard use even though — unlike a\n // fully state-tracked implementation — focus resets to the first item\n // if the user Tabs out and back in rather than resuming where they left\n // off.\n const handleKeyDown = (domEvent: React.KeyboardEvent) => {\n if (domEvent.key === 'Enter' || domEvent.key === ' ') {\n domEvent.preventDefault();\n handleSelect(domEvent);\n return;\n }\n\n if (domEvent.key === 'Escape') {\n if (children?.length && open) {\n domEvent.stopPropagation();\n setOpen(false);\n }\n return;\n }\n\n const isNext = isHorizontal\n ? domEvent.key === 'ArrowRight'\n : domEvent.key === 'ArrowDown';\n const isPrev = isHorizontal\n ? domEvent.key === 'ArrowLeft'\n : domEvent.key === 'ArrowUp';\n\n if (!isNext && !isPrev) {\n return;\n }\n\n domEvent.preventDefault();\n const sibling = isNext\n ? itemRef.current?.nextElementSibling\n : itemRef.current?.previousElementSibling;\n sibling?.querySelector<HTMLElement>('[role=\"menuitem\"]')?.focus();\n };\n\n const childrenContainerClassNames = cn(\n isInline ? 'relative overflow-hidden' : 'absolute min-w-[200px]',\n !isInline && root && isHorizontal\n ? 'left-0 top-full pt-2 pl-0'\n : !isInline\n ? 'left-full top-0 pl-2 pt-0'\n : 'pl-4',\n );\n\n const childrenContainerStyle = isInline\n ? {\n paddingLeft: inlineOffset,\n }\n : root && isHorizontal\n ? {\n paddingTop: top,\n }\n : {\n paddingLeft: left,\n };\n\n return (\n <li\n ref={itemRef}\n role=\"none\"\n className={cn(\n 'group',\n 'relative',\n root && isHorizontal ? 'inline-block' : 'block',\n )}\n onMouseEnter={() => {\n if (isInline) {\n return;\n }\n setOpen(true);\n }}\n onMouseLeave={() => {\n if (isInline) {\n return;\n }\n setOpen(false);\n }}\n onFocus={() => {\n if (isInline) {\n return;\n }\n setOpen(true);\n }}\n onBlur={e => {\n if (isInline) {\n return;\n }\n if (!itemRef.current?.contains(e.relatedTarget as Node)) {\n setOpen(false);\n }\n }}\n >\n <div\n role=\"menuitem\"\n tabIndex={index === 0 ? 0 : -1}\n aria-expanded={children?.length ? open : undefined}\n aria-haspopup={children?.length ? 'menu' : undefined}\n aria-current={selected ? true : undefined}\n className={cn(\n itemClassNames,\n ...(selected\n ? root\n ? ['border-current']\n : [...STYLES.open.classes, ...STYLES.hover.classes]\n : root && !!children?.length\n ? ['group-hover:border-current']\n : []),\n //\n )}\n style={root ? styles?.rootItem : styles?.item}\n onClick={handleSelect}\n onKeyDown={handleKeyDown}\n >\n <div\n className={cn(\n 'w-full',\n 'inline-flex items-center justify-between',\n //\n )}\n >\n <Label className={cn(classNames?.label)} style={styles?.label}>\n {label}\n </Label>\n {!root && !isInline && !!children?.length && (\n <ChevronDown className={cn('-rotate-90')} />\n )}\n {isInline && !!children?.length && (\n <ChevronDown\n className={cn(\n 'transition-all',\n open ? '-rotate-180' : 'rotate-0',\n )}\n />\n )}\n </div>\n </div>\n <AnimatePresence>\n {!!children?.length && open && (\n <motion.div\n className={cn(\n childrenContainerClassNames,\n //\n )}\n style={childrenContainerStyle}\n initial={isInline ? { height: 0, opacity: 0 } : { opacity: 0 }}\n animate={isInline ? { height: 'auto', opacity: 1 } : { opacity: 1 }}\n exit={isInline ? { height: 0, opacity: 0 } : { opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <ul\n role=\"menu\"\n className={cn(\n MENU_CLASSNAMES,\n isInline && 'shadow-none',\n classNames?.subMenu,\n //\n )}\n style={styles?.subMenu}\n >\n {children.map((child, childIndex) => (\n <Item\n {...child}\n key={child.key}\n itemKey={child.key}\n keyPath={[...keyPath, child.key]}\n index={childIndex}\n selectionMap={selectionMap}\n mode={mode}\n classNames={classNames}\n styles={styles}\n offset={offset}\n inlineOffset={inlineOffset}\n onClick={onClick}\n onSelect={onSelect}\n />\n ))}\n </ul>\n </motion.div>\n )}\n </AnimatePresence>\n </li>\n );\n};\n\nexport default Item;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu.d.mts","names":[],"sources":["../../../../src/components/molecules/menu/menu.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"menu.d.mts","names":[],"sources":["../../../../src/components/molecules/menu/menu.tsx"],"mappings":";;;UAUiB,QAAA;EACf,KAAA,EAAO,KAAA,CAAM,SAAA;EACb,GAAA,EAAK,KAAA,CAAM,GAAA;EACX,IAAA;EACA,IAAA;EACA,QAAA,GAAW,QAAA;AAAA;AAAA,UAGI,KAAA;EACf,IAAA;EACA,YAAA,GAAe,KAAA,CAAM,GAAA;EACrB,mBAAA,GAAsB,KAAA,CAAM,GAAA;EAC5B,YAAA,GAAe,YAAA;EACf,MAAA;EACA,YAAA;EACA,UAAA;IACE,QAAA;IACA,OAAA;IACA,IAAA;IACA,KAAA;EAAA;EAEF,MAAA;IACE,QAAA,GAAW,KAAA,CAAM,aAAA;IACjB,OAAA,GAAU,KAAA,CAAM,aAAA;IAChB,IAAA,GAAO,KAAA,CAAM,aAAA;IACb,KAAA,GAAQ,KAAA,CAAM,aAAA;EAAA;EAEhB,OAAA,GAAU,iBAAA;EACV,QAAA,GAAW,iBAAA;AAAA;AAAA,UAGI,SAAA,SAEb,KAAA,EACA,IAAA,CAAK,KAAA,CAAM,wBAAA;EACb,KAAA,GAAQ,QAAA;AAAA;AAAA,KAGE,iBAAA,IAAqB,MAAA;EAC/B,QAAA,EAAU,KAAA,CAAM,cAAA;EAChB,GAAA,EAAK,KAAA,CAAM,GAAA;EACX,OAAA,EAAS,KAAA,CAAM,GAAA;EACf,IAAA,EAAM,QAAA;AAAA;AAAA,cAGK,eAAA;AAAA,KASD,YAAA,GAAe,WAAA,CAAY,KAAA,CAAM,GAAA;AAAA,cAEhC,iBAAA,GACX,KAAA,EAAO,QAAA,IACP,eAAA,EAAiB,WAAA,CAAY,KAAA,CAAM,GAAA,MAClC,YAAA;AAAA,cAeU,OAAA,GACX,IAAA,EAAM,QAAA,EACN,UAAA,EAAY,WAAA,CAAY,KAAA,CAAM,GAAA;AAAA,cAiB1B,IAAA;EAAQ,KAAA;EAAA,IAAA;EAAA,mBAAA;EAAA,YAAA,EAAA,aAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAA,MAAA;EAAA,YAAA;EAAA,OAAA;EAAA,QAAA,EAAA,SAAA;EAAA,GAAA;AAAA,GAaX,SAAA,KAAS,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { cn } from "../../../utils.mjs";
|
|
4
4
|
import Item from "./item/item.mjs";
|
|
5
|
-
import { createElement, useMemo
|
|
5
|
+
import { createElement, useMemo } from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { useControllableState } from "@jbpark/use-hooks";
|
|
7
8
|
|
|
8
9
|
//#region src/components/molecules/menu/menu.tsx
|
|
9
10
|
const MENU_CLASSNAMES = [
|
|
@@ -33,26 +34,27 @@ const findKey = (menu, targetKeys) => {
|
|
|
33
34
|
return false;
|
|
34
35
|
};
|
|
35
36
|
const Menu = ({ items, mode = "vertical", defaultSelectedKeys = [], selectedKeys: _selectedKeys, className, classNames, styles, offset, inlineOffset, onClick, onSelect: _onSelect, ...props }) => {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
const [selectedKeys, setSelectedKeys] = useControllableState({
|
|
38
|
+
value: _selectedKeys,
|
|
39
|
+
defaultValue: defaultSelectedKeys ?? []
|
|
40
|
+
});
|
|
39
41
|
const selectedKeysSet = useMemo(() => new Set(selectedKeys ?? []), [selectedKeys]);
|
|
40
42
|
const selectionMap = useMemo(() => buildSelectionMap(items ?? [], selectedKeysSet), [items, selectedKeysSet]);
|
|
41
43
|
const onSelect = (params) => {
|
|
42
|
-
|
|
44
|
+
setSelectedKeys([params.key]);
|
|
43
45
|
_onSelect?.(params);
|
|
44
46
|
};
|
|
45
47
|
return /* @__PURE__ */ jsx("ul", {
|
|
46
48
|
role: "menu",
|
|
47
49
|
className: cn(MENU_CLASSNAMES, mode === "horizontal" ? "flex" : "inline-block", className),
|
|
48
50
|
...props,
|
|
49
|
-
children: items?.map((item) => /* @__PURE__ */ createElement(Item, {
|
|
51
|
+
children: items?.map((item, index) => /* @__PURE__ */ createElement(Item, {
|
|
50
52
|
root: true,
|
|
51
53
|
...item,
|
|
52
54
|
key: item.key,
|
|
53
55
|
itemKey: item.key,
|
|
54
56
|
keyPath: [item.key],
|
|
55
|
-
|
|
57
|
+
index,
|
|
56
58
|
selectionMap,
|
|
57
59
|
mode,
|
|
58
60
|
classNames,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"menu.mjs","names":[],"sources":["../../../../src/components/molecules/menu/menu.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo
|
|
1
|
+
{"version":3,"file":"menu.mjs","names":[],"sources":["../../../../src/components/molecules/menu/menu.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\n\nimport { useControllableState } from '@jbpark/use-hooks';\n\nimport { cn } from '@repo/ui/utils';\n\nimport Item from './item';\n\nexport interface MenuItem {\n label: React.ReactNode;\n key: React.Key;\n type?: string;\n path?: string;\n children?: MenuItem[];\n}\n\nexport interface Props {\n mode?: 'horizontal' | 'vertical' | 'inline';\n selectedKeys?: React.Key[];\n defaultSelectedKeys?: React.Key[];\n selectionMap?: SelectionMap;\n offset?: [number, number];\n inlineOffset?: number;\n classNames?: {\n rootItem?: string;\n subMenu?: string;\n item?: string;\n label?: string;\n };\n styles?: {\n rootItem?: React.CSSProperties;\n subMenu?: React.CSSProperties;\n item?: React.CSSProperties;\n label?: React.CSSProperties;\n };\n onClick?: ClickEventHandler;\n onSelect?: ClickEventHandler;\n}\n\nexport interface MenuProps\n extends\n Props,\n Omit<React.ComponentPropsWithoutRef<'ul'>, 'onClick' | 'onSelect'> {\n items?: MenuItem[];\n}\n\nexport type ClickEventHandler = (params: {\n domEvent: React.SyntheticEvent;\n key: React.Key;\n keyPath: React.Key[];\n item: MenuItem;\n}) => void;\n\nexport const MENU_CLASSNAMES = [\n 'p-0',\n 'bg-popover',\n 'text-popover-foreground',\n 'shadow-md',\n 'rounded-md',\n //\n];\n\nexport type SelectionMap = ReadonlyMap<React.Key, boolean>;\n\nexport const buildSelectionMap = (\n items: MenuItem[],\n selectedKeysSet: ReadonlySet<React.Key>,\n): SelectionMap => {\n const map = new Map<React.Key, boolean>();\n\n const dfs = (node: MenuItem): boolean => {\n const self = selectedKeysSet.has(node.key);\n const hasSelectedChild = node.children?.some(child => dfs(child)) ?? false;\n const isSelected = self || hasSelectedChild;\n map.set(node.key, isSelected);\n return isSelected;\n };\n\n items.forEach(dfs);\n return map;\n};\n\nexport const findKey = (\n menu: MenuItem,\n targetKeys: ReadonlySet<React.Key>,\n): boolean => {\n if (targetKeys.has(menu.key)) {\n return true;\n }\n\n if (menu.children && Array.isArray(menu.children)) {\n for (const child of menu.children) {\n if (findKey(child, targetKeys)) {\n return true;\n }\n }\n }\n\n return false;\n};\n\nconst Menu = ({\n items,\n mode = 'vertical',\n defaultSelectedKeys = [],\n selectedKeys: _selectedKeys,\n className,\n classNames,\n styles,\n offset,\n inlineOffset,\n onClick,\n onSelect: _onSelect,\n ...props\n}: MenuProps) => {\n const [selectedKeys, setSelectedKeys] = useControllableState<React.Key[]>({\n value: _selectedKeys,\n defaultValue: defaultSelectedKeys ?? [],\n });\n\n const selectedKeysSet = useMemo(\n () => new Set<React.Key>(selectedKeys ?? []),\n [selectedKeys],\n );\n\n const selectionMap = useMemo(\n () => buildSelectionMap(items ?? [], selectedKeysSet),\n [items, selectedKeysSet],\n );\n\n const onSelect = (params: {\n domEvent: React.SyntheticEvent;\n key: React.Key;\n keyPath: React.Key[];\n item: MenuItem;\n }) => {\n setSelectedKeys([params.key]);\n _onSelect?.(params);\n };\n\n return (\n <ul\n role=\"menu\"\n className={cn(\n MENU_CLASSNAMES,\n mode === 'horizontal' ? 'flex' : 'inline-block',\n className,\n )}\n {...props}\n >\n {items?.map((item, index) => (\n <Item\n root\n {...item}\n key={item.key}\n itemKey={item.key}\n keyPath={[item.key]}\n index={index}\n selectionMap={selectionMap}\n mode={mode}\n classNames={classNames}\n styles={styles}\n offset={offset}\n inlineOffset={inlineOffset}\n onClick={onClick}\n onSelect={onSelect}\n />\n ))}\n </ul>\n );\n};\n\nexport default Menu;\n"],"mappings":";;;;;;;;;AAuDA,MAAa,kBAAkB;CAC7B;CACA;CACA;CACA;CACA;CAED;AAID,MAAa,qBACX,OACA,oBACiB;CACjB,MAAM,sBAAM,IAAI,KAAyB;CAEzC,MAAM,OAAO,SAA4B;EACvC,MAAM,OAAO,gBAAgB,IAAI,KAAK,IAAI;EAC1C,MAAM,mBAAmB,KAAK,UAAU,MAAK,UAAS,IAAI,MAAM,CAAC,IAAI;EACrE,MAAM,aAAa,QAAQ;AAC3B,MAAI,IAAI,KAAK,KAAK,WAAW;AAC7B,SAAO;;AAGT,OAAM,QAAQ,IAAI;AAClB,QAAO;;AAGT,MAAa,WACX,MACA,eACY;AACZ,KAAI,WAAW,IAAI,KAAK,IAAI,CAC1B,QAAO;AAGT,KAAI,KAAK,YAAY,MAAM,QAAQ,KAAK,SAAS,EAC/C;OAAK,MAAM,SAAS,KAAK,SACvB,KAAI,QAAQ,OAAO,WAAW,CAC5B,QAAO;;AAKb,QAAO;;AAGT,MAAM,QAAQ,EACZ,OACA,OAAO,YACP,sBAAsB,EAAE,EACxB,cAAc,eACd,WACA,YACA,QACA,QACA,cACA,SACA,UAAU,WACV,GAAG,YACY;CACf,MAAM,CAAC,cAAc,mBAAmB,qBAAkC;EACxE,OAAO;EACP,cAAc,uBAAuB,EAAE;EACxC,CAAC;CAEF,MAAM,kBAAkB,cAChB,IAAI,IAAe,gBAAgB,EAAE,CAAC,EAC5C,CAAC,aAAa,CACf;CAED,MAAM,eAAe,cACb,kBAAkB,SAAS,EAAE,EAAE,gBAAgB,EACrD,CAAC,OAAO,gBAAgB,CACzB;CAED,MAAM,YAAY,WAKZ;AACJ,kBAAgB,CAAC,OAAO,IAAI,CAAC;AAC7B,cAAY,OAAO;;AAGrB,QACE,oBAAC;EACC,MAAK;EACL,WAAW,GACT,iBACA,SAAS,eAAe,SAAS,gBACjC,UACD;EACD,GAAI;YAEH,OAAO,KAAK,MAAM,UACjB,8BAAC;GACC;GACA,GAAI;GACJ,KAAK,KAAK;GACV,SAAS,KAAK;GACd,SAAS,CAAC,KAAK,IAAI;GACZ;GACO;GACR;GACM;GACJ;GACA;GACM;GACL;GACC;IACV,CACF;GACC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { cn } from "../../../utils.mjs";
|
|
4
4
|
import { resizable_exports } from "../../../core/resizable.mjs";
|
|
5
|
-
import { Children, Fragment } from "react";
|
|
5
|
+
import { Children, Fragment, isValidElement } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
|
|
8
8
|
//#region src/components/molecules/splitter/splitter.tsx
|
|
@@ -20,7 +20,7 @@ const Splitter = ({ children, className, orientation = "horizontal", withHandle
|
|
|
20
20
|
transition-colors`, handleClassName),
|
|
21
21
|
withHandle: withHandle && handle === void 0,
|
|
22
22
|
children: handle
|
|
23
|
-
})] }, index))
|
|
23
|
+
})] }, isValidElement(panel) && panel.key != null ? panel.key : index))
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"splitter.mjs","names":["resizable"],"sources":["../../../../src/components/molecules/splitter/splitter.tsx"],"sourcesContent":["'use client';\n\nimport { Children, Fragment } from 'react';\n\nimport { resizable } from '@repo/ui/core';\nimport { cn } from '@repo/ui/utils';\n\nconst { ResizableHandle, ResizablePanelGroup } = resizable;\n\ntype SplitterLayout = Parameters<\n NonNullable<\n React.ComponentProps<typeof ResizablePanelGroup>['onLayoutChanged']\n >\n>[0];\n\nexport interface Props extends Omit<\n React.ComponentProps<typeof ResizablePanelGroup>,\n 'children' | 'orientation' | 'onLayoutChange' | 'onLayoutChanged' | 'onResize'\n> {\n children: React.ReactNode;\n orientation?: 'horizontal' | 'vertical';\n withHandle?: boolean;\n handle?: React.ReactNode;\n handleClassName?: string;\n onResize?: (layout: SplitterLayout) => void;\n onResizeEnd?: (layout: SplitterLayout) => void;\n}\n\nconst Splitter = ({\n children,\n className,\n orientation = 'horizontal',\n withHandle = true,\n handle,\n handleClassName,\n onResize,\n onResizeEnd,\n ...props\n}: Props) => {\n const panels = Children.toArray(children);\n\n return (\n <ResizablePanelGroup\n className={cn(\n 'border-border/60 h-full w-full rounded-lg border',\n className,\n )}\n orientation={orientation}\n onLayoutChange={onResize}\n onLayoutChanged={onResizeEnd}\n {...props}\n >\n {panels.map((panel, index) => (\n <Fragment
|
|
1
|
+
{"version":3,"file":"splitter.mjs","names":["resizable"],"sources":["../../../../src/components/molecules/splitter/splitter.tsx"],"sourcesContent":["'use client';\n\nimport { Children, Fragment, isValidElement } from 'react';\n\nimport { resizable } from '@repo/ui/core';\nimport { cn } from '@repo/ui/utils';\n\nconst { ResizableHandle, ResizablePanelGroup } = resizable;\n\ntype SplitterLayout = Parameters<\n NonNullable<\n React.ComponentProps<typeof ResizablePanelGroup>['onLayoutChanged']\n >\n>[0];\n\nexport interface Props extends Omit<\n React.ComponentProps<typeof ResizablePanelGroup>,\n 'children' | 'orientation' | 'onLayoutChange' | 'onLayoutChanged' | 'onResize'\n> {\n children: React.ReactNode;\n orientation?: 'horizontal' | 'vertical';\n withHandle?: boolean;\n handle?: React.ReactNode;\n handleClassName?: string;\n onResize?: (layout: SplitterLayout) => void;\n onResizeEnd?: (layout: SplitterLayout) => void;\n}\n\nconst Splitter = ({\n children,\n className,\n orientation = 'horizontal',\n withHandle = true,\n handle,\n handleClassName,\n onResize,\n onResizeEnd,\n ...props\n}: Props) => {\n const panels = Children.toArray(children);\n\n return (\n <ResizablePanelGroup\n className={cn(\n 'border-border/60 h-full w-full rounded-lg border',\n className,\n )}\n orientation={orientation}\n onLayoutChange={onResize}\n onLayoutChanged={onResizeEnd}\n {...props}\n >\n {panels.map((panel, index) => (\n <Fragment\n key={isValidElement(panel) && panel.key != null ? panel.key : index}\n >\n {panel}\n {index < panels.length - 1 && (\n <ResizableHandle\n className={cn(\n `hover:bg-border/80 data-separator-active:bg-border\n transition-colors`,\n handleClassName,\n )}\n withHandle={withHandle && handle === undefined}\n >\n {handle}\n </ResizableHandle>\n )}\n </Fragment>\n ))}\n </ResizablePanelGroup>\n );\n};\n\nexport default Splitter;\n"],"mappings":";;;;;;;;AAOA,MAAM,EAAE,iBAAiB,wBAAwBA;AAqBjD,MAAM,YAAY,EAChB,UACA,WACA,cAAc,cACd,aAAa,MACb,QACA,iBACA,UACA,aACA,GAAG,YACQ;CACX,MAAM,SAAS,SAAS,QAAQ,SAAS;AAEzC,QACE,oBAAC;EACC,WAAW,GACT,oDACA,UACD;EACY;EACb,gBAAgB;EAChB,iBAAiB;EACjB,GAAI;YAEH,OAAO,KAAK,OAAO,UAClB,qBAAC,uBAGE,OACA,QAAQ,OAAO,SAAS,KACvB,oBAAC;GACC,WAAW,GACT;oCAEA,gBACD;GACD,YAAY,cAAc,WAAW;aAEpC;IACe,KAbf,eAAe,MAAM,IAAI,MAAM,OAAO,OAAO,MAAM,MAAM,MAerD,CACX;GACkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.d.mts","names":[],"sources":["../../../src/components/organisms/drawer.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"drawer.d.mts","names":[],"sources":["../../../src/components/organisms/drawer.tsx"],"mappings":";;;UA2CiB,KAAA;EACf,IAAA;EACA,QAAA,GAAW,KAAA,CAAM,SAAA;EACjB,KAAA,GAAQ,KAAA,CAAM,SAAA;EACd,KAAA,GAAQ,KAAA,CAAM,SAAA;EACd,MAAA,GAAS,KAAA,CAAM,SAAA;EACf,QAAA;EACA,SAAA,GAAY,KAAA,CAAM,SAAA;EAClB,SAAA;EACA,IAAA;EACA,YAAA;EACA,SAAA;EACA,SAAA;EACA,OAAA;EACA,IAAA;EACA,SAAA;EACA,KAAA,GAAQ,KAAA,CAAM,aAAA;EACd,UAAA;IACE,IAAA;IACA,OAAA;IACA,SAAA;IACA,MAAA;IACA,KAAA;IACA,KAAA;IACA,KAAA;IACA,IAAA;IACA,MAAA;EAAA;EAEF,SAAA,GAAY,WAAA;EACZ,OAAA;AAAA;AAAA,cA8BI,MAAA;EAAU,IAAA;EAAA,KAAA;EAAA,KAAA;EAAA,MAAA;EAAA,QAAA;EAAA,SAAA;EAAA,SAAA;EAAA,IAAA;EAAA,YAAA;EAAA,SAAA;EAAA,SAAA;EAAA,OAAA;EAAA,IAAA;EAAA,SAAA;EAAA,UAAA;EAAA,KAAA;EAAA,QAAA;EAAA,SAAA;EAAA,OAAA;EAAA,GAAA;AAAA,GAqBb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { cn, renderConditional } from "../../utils.mjs";
|
|
4
4
|
import { drawer_exports } from "../../core/drawer.mjs";
|
|
5
5
|
import Button from "../../Button.mjs";
|
|
6
|
+
import { DEFAULT_LOCALE, useConfig } from "../../providers/Config/context.mjs";
|
|
6
7
|
import { X } from "lucide-react";
|
|
7
8
|
import { useEffect } from "react";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -38,6 +39,7 @@ const getSizeStyles = (direction, size) => {
|
|
|
38
39
|
return direction === "top" || direction === "bottom" ? { height: value } : { width: value };
|
|
39
40
|
};
|
|
40
41
|
const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direction = "bottom", size = "medium", maskClosable = true, handlebar = true, draggable = false, rounded = false, mask = true, className, classNames, style, children, container, onClose, ...props }) => {
|
|
42
|
+
const { locale } = useConfig();
|
|
41
43
|
useEffect(() => {
|
|
42
44
|
if (!open || mask) return;
|
|
43
45
|
const targetDocument = container?.ownerDocument ?? document;
|
|
@@ -95,7 +97,7 @@ const Drawer = ({ open, title, extra, footer, closable = true, closeIcon, direct
|
|
|
95
97
|
type: "text",
|
|
96
98
|
shape: "circle",
|
|
97
99
|
size: "small",
|
|
98
|
-
"aria-label":
|
|
100
|
+
"aria-label": locale.close ?? DEFAULT_LOCALE.close,
|
|
99
101
|
icon: closeIcon || /* @__PURE__ */ jsx(X, {}),
|
|
100
102
|
onClick: onClose,
|
|
101
103
|
className: cn(classNames?.close)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.mjs","names":["drawer"],"sources":["../../../src/components/organisms/drawer.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { X } from 'lucide-react';\n\nimport { drawer } from '@repo/ui/core';\nimport { cn, renderConditional } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nconst {\n DrawerContent,\n Drawer: DrawerCore,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} = drawer;\n\n// `body.style.pointerEvents` is global state (per document), so several\n// mask-less overlays opening at once would otherwise race: each instance\n// would capture whatever the previous one had already written and the\n// last cleanup would restore a stale value. Reference counting makes only\n// the first instance capture the original value and only the last one\n// restore it. Keyed by document (not a single module-level counter) so\n// drawers portaled into different documents — e.g. live-editor's iframe\n// Renderer — don't share state with the host document.\ntype MaskLessState = { count: number; originalPointerEvents: string | null };\nconst maskLessStates = new WeakMap<Document, MaskLessState>();\n\nconst getMaskLessState = (doc: Document): MaskLessState => {\n let state = maskLessStates.get(doc);\n\n if (!state) {\n state = { count: 0, originalPointerEvents: null };\n maskLessStates.set(doc, state);\n }\n\n return state;\n};\n\nexport interface Props {\n open: boolean;\n children?: React.ReactNode;\n title?: React.ReactNode;\n extra?: React.ReactNode;\n footer?: React.ReactNode;\n closable?: boolean;\n closeIcon?: React.ReactNode;\n direction?: 'top' | 'bottom' | 'left' | 'right';\n size?: 'small' | 'medium' | 'large' | 'full' | string;\n maskClosable?: boolean;\n handlebar?: boolean;\n draggable?: boolean;\n rounded?: boolean;\n mask?: boolean;\n className?: string;\n style?: React.CSSProperties;\n classNames?: {\n mask?: string;\n content?: string;\n handlebar?: string;\n header?: string;\n title?: string;\n close?: string;\n extra?: string;\n body?: string;\n footer?: string;\n };\n container?: HTMLElement;\n onClose: () => void;\n}\n\nconst SIZES: Record<string, string> = {\n small: '30%',\n medium: '50%',\n large: '75%',\n full: '90%',\n};\n\n// Rounds the edge facing into the viewport — the opposite edge from\n// whichever side the drawer is anchored to.\nconst ROUNDED_CLASSES: Record<'top' | 'bottom' | 'left' | 'right', string> = {\n top: 'rounded-b-[30px]!',\n bottom: 'rounded-t-[30px]!',\n left: 'rounded-r-[30px]!',\n right: 'rounded-l-[30px]!',\n};\n\nconst getSizeStyles = (\n direction: 'top' | 'bottom' | 'left' | 'right',\n size: string,\n): React.CSSProperties => {\n const value = SIZES[size] || size;\n\n return direction === 'top' || direction === 'bottom'\n ? { height: value }\n : { width: value };\n};\n\nconst Drawer = ({\n open,\n title,\n extra,\n footer,\n closable = true,\n closeIcon,\n direction = 'bottom',\n size = 'medium',\n maskClosable = true,\n handlebar = true,\n draggable = false,\n rounded = false,\n mask = true,\n className,\n classNames,\n style,\n children,\n container,\n onClose,\n ...props\n}: Props) => {\n useEffect(() => {\n if (!open || mask) {\n return;\n }\n\n const targetDocument = container?.ownerDocument ?? document;\n const targetWindow = targetDocument.defaultView ?? window;\n const body = targetDocument.body;\n const state = getMaskLessState(targetDocument);\n\n if (state.count === 0) {\n state.originalPointerEvents = body.style.pointerEvents;\n }\n state.count += 1;\n\n const raf = targetWindow.requestAnimationFrame(() => {\n body.style.pointerEvents = 'auto';\n });\n\n return () => {\n targetWindow.cancelAnimationFrame(raf);\n state.count -= 1;\n\n if (state.count === 0) {\n body.style.pointerEvents = state.originalPointerEvents ?? '';\n state.originalPointerEvents = null;\n }\n };\n }, [open, mask, container]);\n\n return (\n <DrawerCore\n open={open}\n direction={direction}\n handleOnly={!draggable}\n container={container}\n onOpenChange={open => {\n if (!open) {\n onClose();\n }\n }}\n {...props}\n >\n <DrawerContent\n className={cn(\n 'border-none outline-none',\n rounded ? ROUNDED_CLASSES[direction] : 'rounded-none!',\n classNames?.content,\n className,\n //\n )}\n handlebar={handlebar}\n mask={mask}\n onPointerDownOutside={event => {\n if (!maskClosable) {\n event.preventDefault();\n }\n }}\n classNames={{\n mask: classNames?.mask || '',\n handlebar: classNames?.handlebar || '',\n }}\n style={{\n ...getSizeStyles(direction, size),\n ...style,\n }}\n >\n <DrawerHeader\n className={cn(\n classNames?.header,\n !title && 'p-0',\n //\n )}\n >\n <div className=\"flex items-start gap-2\">\n {closable && (\n <Button\n type=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label
|
|
1
|
+
{"version":3,"file":"drawer.mjs","names":["drawer"],"sources":["../../../src/components/organisms/drawer.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { X } from 'lucide-react';\n\nimport { drawer } from '@repo/ui/core';\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn, renderConditional } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\n\nconst {\n DrawerContent,\n Drawer: DrawerCore,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerTitle,\n} = drawer;\n\n// `body.style.pointerEvents` is global state (per document), so several\n// mask-less overlays opening at once would otherwise race: each instance\n// would capture whatever the previous one had already written and the\n// last cleanup would restore a stale value. Reference counting makes only\n// the first instance capture the original value and only the last one\n// restore it. Keyed by document (not a single module-level counter) so\n// drawers portaled into different documents — e.g. live-editor's iframe\n// Renderer — don't share state with the host document.\ntype MaskLessState = { count: number; originalPointerEvents: string | null };\nconst maskLessStates = new WeakMap<Document, MaskLessState>();\n\nconst getMaskLessState = (doc: Document): MaskLessState => {\n let state = maskLessStates.get(doc);\n\n if (!state) {\n state = { count: 0, originalPointerEvents: null };\n maskLessStates.set(doc, state);\n }\n\n return state;\n};\n\nexport interface Props {\n open: boolean;\n children?: React.ReactNode;\n title?: React.ReactNode;\n extra?: React.ReactNode;\n footer?: React.ReactNode;\n closable?: boolean;\n closeIcon?: React.ReactNode;\n direction?: 'top' | 'bottom' | 'left' | 'right';\n size?: 'small' | 'medium' | 'large' | 'full' | string;\n maskClosable?: boolean;\n handlebar?: boolean;\n draggable?: boolean;\n rounded?: boolean;\n mask?: boolean;\n className?: string;\n style?: React.CSSProperties;\n classNames?: {\n mask?: string;\n content?: string;\n handlebar?: string;\n header?: string;\n title?: string;\n close?: string;\n extra?: string;\n body?: string;\n footer?: string;\n };\n container?: HTMLElement;\n onClose: () => void;\n}\n\nconst SIZES: Record<string, string> = {\n small: '30%',\n medium: '50%',\n large: '75%',\n full: '90%',\n};\n\n// Rounds the edge facing into the viewport — the opposite edge from\n// whichever side the drawer is anchored to.\nconst ROUNDED_CLASSES: Record<'top' | 'bottom' | 'left' | 'right', string> = {\n top: 'rounded-b-[30px]!',\n bottom: 'rounded-t-[30px]!',\n left: 'rounded-r-[30px]!',\n right: 'rounded-l-[30px]!',\n};\n\nconst getSizeStyles = (\n direction: 'top' | 'bottom' | 'left' | 'right',\n size: string,\n): React.CSSProperties => {\n const value = SIZES[size] || size;\n\n return direction === 'top' || direction === 'bottom'\n ? { height: value }\n : { width: value };\n};\n\nconst Drawer = ({\n open,\n title,\n extra,\n footer,\n closable = true,\n closeIcon,\n direction = 'bottom',\n size = 'medium',\n maskClosable = true,\n handlebar = true,\n draggable = false,\n rounded = false,\n mask = true,\n className,\n classNames,\n style,\n children,\n container,\n onClose,\n ...props\n}: Props) => {\n const { locale } = useConfig();\n\n useEffect(() => {\n if (!open || mask) {\n return;\n }\n\n const targetDocument = container?.ownerDocument ?? document;\n const targetWindow = targetDocument.defaultView ?? window;\n const body = targetDocument.body;\n const state = getMaskLessState(targetDocument);\n\n if (state.count === 0) {\n state.originalPointerEvents = body.style.pointerEvents;\n }\n state.count += 1;\n\n const raf = targetWindow.requestAnimationFrame(() => {\n body.style.pointerEvents = 'auto';\n });\n\n return () => {\n targetWindow.cancelAnimationFrame(raf);\n state.count -= 1;\n\n if (state.count === 0) {\n body.style.pointerEvents = state.originalPointerEvents ?? '';\n state.originalPointerEvents = null;\n }\n };\n }, [open, mask, container]);\n\n return (\n <DrawerCore\n open={open}\n direction={direction}\n handleOnly={!draggable}\n container={container}\n onOpenChange={open => {\n if (!open) {\n onClose();\n }\n }}\n {...props}\n >\n <DrawerContent\n className={cn(\n 'border-none outline-none',\n rounded ? ROUNDED_CLASSES[direction] : 'rounded-none!',\n classNames?.content,\n className,\n //\n )}\n handlebar={handlebar}\n mask={mask}\n onPointerDownOutside={event => {\n if (!maskClosable) {\n event.preventDefault();\n }\n }}\n classNames={{\n mask: classNames?.mask || '',\n handlebar: classNames?.handlebar || '',\n }}\n style={{\n ...getSizeStyles(direction, size),\n ...style,\n }}\n >\n <DrawerHeader\n className={cn(\n classNames?.header,\n !title && 'p-0',\n //\n )}\n >\n <div className=\"flex items-start gap-2\">\n {closable && (\n <Button\n type=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={closeIcon || <X />}\n onClick={onClose}\n className={cn(classNames?.close)}\n />\n )}\n <div className=\"flex flex-1 items-start justify-between gap-2\">\n <DrawerTitle className={cn(classNames?.title)}>\n {title}\n </DrawerTitle>\n {renderConditional(extra, v => (\n <div className={cn('shrink-0', classNames?.extra)}>{v}</div>\n ))}\n </div>\n </div>\n <DrawerDescription className=\"hidden\" />\n </DrawerHeader>\n <div\n className={cn(\n 'overflow-auto p-5',\n classNames?.body,\n //\n )}\n >\n {children}\n </div>\n {renderConditional(footer, v => (\n <DrawerFooter className={cn(classNames?.footer)}>{v}</DrawerFooter>\n ))}\n </DrawerContent>\n </DrawerCore>\n );\n};\n\nexport default Drawer;\n"],"mappings":";;;;;;;;;;;AAYA,MAAM,EACJ,eACA,QAAQ,YACR,mBACA,cACA,cACA,gBACEA;AAWJ,MAAM,iCAAiB,IAAI,SAAkC;AAE7D,MAAM,oBAAoB,QAAiC;CACzD,IAAI,QAAQ,eAAe,IAAI,IAAI;AAEnC,KAAI,CAAC,OAAO;AACV,UAAQ;GAAE,OAAO;GAAG,uBAAuB;GAAM;AACjD,iBAAe,IAAI,KAAK,MAAM;;AAGhC,QAAO;;AAmCT,MAAM,QAAgC;CACpC,OAAO;CACP,QAAQ;CACR,OAAO;CACP,MAAM;CACP;AAID,MAAM,kBAAuE;CAC3E,KAAK;CACL,QAAQ;CACR,MAAM;CACN,OAAO;CACR;AAED,MAAM,iBACJ,WACA,SACwB;CACxB,MAAM,QAAQ,MAAM,SAAS;AAE7B,QAAO,cAAc,SAAS,cAAc,WACxC,EAAE,QAAQ,OAAO,GACjB,EAAE,OAAO,OAAO;;AAGtB,MAAM,UAAU,EACd,MACA,OACA,OACA,QACA,WAAW,MACX,WACA,YAAY,UACZ,OAAO,UACP,eAAe,MACf,YAAY,MACZ,YAAY,OACZ,UAAU,OACV,OAAO,MACP,WACA,YACA,OACA,UACA,WACA,SACA,GAAG,YACQ;CACX,MAAM,EAAE,WAAW,WAAW;AAE9B,iBAAgB;AACd,MAAI,CAAC,QAAQ,KACX;EAGF,MAAM,iBAAiB,WAAW,iBAAiB;EACnD,MAAM,eAAe,eAAe,eAAe;EACnD,MAAM,OAAO,eAAe;EAC5B,MAAM,QAAQ,iBAAiB,eAAe;AAE9C,MAAI,MAAM,UAAU,EAClB,OAAM,wBAAwB,KAAK,MAAM;AAE3C,QAAM,SAAS;EAEf,MAAM,MAAM,aAAa,4BAA4B;AACnD,QAAK,MAAM,gBAAgB;IAC3B;AAEF,eAAa;AACX,gBAAa,qBAAqB,IAAI;AACtC,SAAM,SAAS;AAEf,OAAI,MAAM,UAAU,GAAG;AACrB,SAAK,MAAM,gBAAgB,MAAM,yBAAyB;AAC1D,UAAM,wBAAwB;;;IAGjC;EAAC;EAAM;EAAM;EAAU,CAAC;AAE3B,QACE,oBAAC;EACO;EACK;EACX,YAAY,CAAC;EACF;EACX,eAAc,SAAQ;AACpB,OAAI,CAAC,KACH,UAAS;;EAGb,GAAI;YAEJ,qBAAC;GACC,WAAW,GACT,4BACA,UAAU,gBAAgB,aAAa,iBACvC,YAAY,SACZ,UAED;GACU;GACL;GACN,uBAAsB,UAAS;AAC7B,QAAI,CAAC,aACH,OAAM,gBAAgB;;GAG1B,YAAY;IACV,MAAM,YAAY,QAAQ;IAC1B,WAAW,YAAY,aAAa;IACrC;GACD,OAAO;IACL,GAAG,cAAc,WAAW,KAAK;IACjC,GAAG;IACJ;;IAED,qBAAC;KACC,WAAW,GACT,YAAY,QACZ,CAAC,SAAS,MAEX;gBAED,qBAAC;MAAI,WAAU;iBACZ,YACC,oBAAC;OACC,MAAK;OACL,OAAM;OACN,MAAK;OACL,cAAY,OAAO,SAAS,eAAe;OAC3C,MAAM,aAAa,oBAAC,MAAI;OACxB,SAAS;OACT,WAAW,GAAG,YAAY,MAAM;QAChC,EAEJ,qBAAC;OAAI,WAAU;kBACb,oBAAC;QAAY,WAAW,GAAG,YAAY,MAAM;kBAC1C;SACW,EACb,kBAAkB,QAAO,MACxB,oBAAC;QAAI,WAAW,GAAG,YAAY,YAAY,MAAM;kBAAG;SAAQ,CAC5D;QACE;OACF,EACN,oBAAC,qBAAkB,WAAU,WAAW;MAC3B;IACf,oBAAC;KACC,WAAW,GACT,qBACA,YAAY,KAEb;KAEA;MACG;IACL,kBAAkB,SAAQ,MACzB,oBAAC;KAAa,WAAW,GAAG,YAAY,OAAO;eAAG;MAAiB,CACnE;;IACY;GACL"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
5
|
+
import { v4 } from "uuid";
|
|
6
|
+
import { createRoot } from "react-dom/client";
|
|
7
|
+
|
|
8
|
+
//#region src/components/organisms/imperative-stack.tsx
|
|
9
|
+
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
10
|
+
function createImperativeStack({ createRootElement, StackItem }) {
|
|
11
|
+
const containerStates = /* @__PURE__ */ new Map();
|
|
12
|
+
const rootElements = /* @__PURE__ */ new WeakMap();
|
|
13
|
+
const reactRoots = /* @__PURE__ */ new Map();
|
|
14
|
+
const getContainerState = (container) => {
|
|
15
|
+
let state = containerStates.get(container);
|
|
16
|
+
if (!state) {
|
|
17
|
+
state = {
|
|
18
|
+
stack: [],
|
|
19
|
+
update: null
|
|
20
|
+
};
|
|
21
|
+
containerStates.set(container, state);
|
|
22
|
+
}
|
|
23
|
+
return state;
|
|
24
|
+
};
|
|
25
|
+
const getRootElement = (container) => {
|
|
26
|
+
if (!isBrowser) return null;
|
|
27
|
+
const targetContainer = container || document.body;
|
|
28
|
+
let rootEl = rootElements.get(targetContainer);
|
|
29
|
+
if (!rootEl) {
|
|
30
|
+
rootEl = createRootElement();
|
|
31
|
+
targetContainer.appendChild(rootEl);
|
|
32
|
+
rootElements.set(targetContainer, rootEl);
|
|
33
|
+
}
|
|
34
|
+
return rootEl;
|
|
35
|
+
};
|
|
36
|
+
const StackRenderer = ({ container }) => {
|
|
37
|
+
const [, forceUpdate] = useState({});
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const state = getContainerState(container);
|
|
40
|
+
state.update = () => forceUpdate({});
|
|
41
|
+
return () => {
|
|
42
|
+
state.update = null;
|
|
43
|
+
};
|
|
44
|
+
}, [container]);
|
|
45
|
+
const { stack } = getContainerState(container);
|
|
46
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: stack.map(({ id, ...props }) => /* @__PURE__ */ jsx(StackItem, {
|
|
47
|
+
id,
|
|
48
|
+
...props
|
|
49
|
+
}, id)) });
|
|
50
|
+
};
|
|
51
|
+
const render = (props) => {
|
|
52
|
+
if (!isBrowser) return;
|
|
53
|
+
const targetContainer = props.container || document.body;
|
|
54
|
+
const rootElement = getRootElement(targetContainer);
|
|
55
|
+
if (!reactRoots.has(targetContainer)) {
|
|
56
|
+
const root = createRoot(rootElement);
|
|
57
|
+
reactRoots.set(targetContainer, root);
|
|
58
|
+
root.render(/* @__PURE__ */ jsx(StackRenderer, { container: targetContainer }));
|
|
59
|
+
}
|
|
60
|
+
const state = getContainerState(targetContainer);
|
|
61
|
+
const id = props.id || v4();
|
|
62
|
+
state.stack.push({
|
|
63
|
+
...props,
|
|
64
|
+
id
|
|
65
|
+
});
|
|
66
|
+
state.update?.();
|
|
67
|
+
return id;
|
|
68
|
+
};
|
|
69
|
+
const destroy = (id) => {
|
|
70
|
+
if (!id) {
|
|
71
|
+
containerStates.forEach((state) => {
|
|
72
|
+
state.stack = [];
|
|
73
|
+
state.update?.();
|
|
74
|
+
});
|
|
75
|
+
reactRoots.forEach((root, container) => {
|
|
76
|
+
root.unmount();
|
|
77
|
+
rootElements.get(container)?.remove();
|
|
78
|
+
rootElements.delete(container);
|
|
79
|
+
});
|
|
80
|
+
reactRoots.clear();
|
|
81
|
+
containerStates.clear();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
containerStates.forEach((state, container) => {
|
|
85
|
+
if (!state.stack.some((item) => item.id === id)) return;
|
|
86
|
+
state.stack = state.stack.filter((item) => item.id !== id);
|
|
87
|
+
state.update?.();
|
|
88
|
+
if (!state.stack.length) {
|
|
89
|
+
reactRoots.get(container)?.unmount();
|
|
90
|
+
reactRoots.delete(container);
|
|
91
|
+
containerStates.delete(container);
|
|
92
|
+
rootElements.get(container)?.remove();
|
|
93
|
+
rootElements.delete(container);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
render,
|
|
99
|
+
destroy,
|
|
100
|
+
getRootElement
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
export { createImperativeStack, isBrowser };
|
|
106
|
+
//# sourceMappingURL=imperative-stack.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imperative-stack.mjs","names":["uuid"],"sources":["../../../src/components/organisms/imperative-stack.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Root, createRoot } from 'react-dom/client';\n\nimport { v4 as uuid } from 'uuid';\n\n// Shared machinery behind Modal.info/success/error/warning/confirm and\n// Toast.info/success/error/warning — both used to independently\n// reimplement ~120 near-identical lines of this (container-scoped stack,\n// a React root per container, imperative render/destroy). The only parts\n// that actually differ between the two are the DOM node their stack\n// mounts into (position/ARIA) and which component renders each stack\n// entry — everything else here is generic over that.\n\nexport const isBrowser =\n typeof window !== 'undefined' && typeof document !== 'undefined';\n\ntype ContainerState<TProps> = {\n stack: (TProps & { id: string })[];\n update: (() => void) | null;\n};\n\nexport interface ImperativeStack<\n TProps extends { id?: string; container?: HTMLElement },\n> {\n render: (props: TProps) => string | undefined;\n destroy: (id?: string) => void;\n getRootElement: (container?: HTMLElement) => HTMLElement | null;\n}\n\nexport interface ImperativeStackOptions<\n TProps extends { id?: string; container?: HTMLElement },\n> {\n /** Creates the DOM node this stack's React root mounts into — not yet\n * attached to the document; the factory appends it to the target\n * container itself. Called once per distinct container. */\n createRootElement: () => HTMLElement;\n /** Renders one stack entry — typically each caller's \"Static<Thing>\"\n * component, which owns its own createPortal(..., getRootElement()). */\n StackItem: React.ComponentType<TProps & { id: string }>;\n}\n\nexport function createImperativeStack<\n TProps extends { id?: string; container?: HTMLElement },\n>({\n createRootElement,\n StackItem,\n}: ImperativeStackOptions<TProps>): ImperativeStack<TProps> {\n const containerStates = new Map<HTMLElement, ContainerState<TProps>>();\n const rootElements = new WeakMap<HTMLElement, HTMLElement>();\n const reactRoots = new Map<HTMLElement, Root>();\n\n const getContainerState = (\n container: HTMLElement,\n ): ContainerState<TProps> => {\n let state = containerStates.get(container);\n\n if (!state) {\n state = { stack: [], update: null };\n containerStates.set(container, state);\n }\n\n return state;\n };\n\n const getRootElement = (container?: HTMLElement): HTMLElement | null => {\n if (!isBrowser) {\n return null;\n }\n\n const targetContainer = container || document.body;\n let rootEl = rootElements.get(targetContainer);\n\n if (!rootEl) {\n rootEl = createRootElement();\n targetContainer.appendChild(rootEl);\n rootElements.set(targetContainer, rootEl);\n }\n\n return rootEl;\n };\n\n const StackRenderer = ({ container }: { container: HTMLElement }) => {\n const [, forceUpdate] = useState({});\n\n useEffect(() => {\n const state = getContainerState(container);\n state.update = () => forceUpdate({});\n return () => {\n state.update = null;\n };\n }, [container]);\n\n const { stack } = getContainerState(container);\n\n return (\n <>\n {stack.map(({ id, ...props }) => (\n <StackItem key={id} id={id} {...(props as TProps)} />\n ))}\n </>\n );\n };\n\n const render = (props: TProps): string | undefined => {\n if (!isBrowser) {\n return;\n }\n\n const targetContainer = props.container || document.body;\n const rootElement = getRootElement(targetContainer)!;\n\n if (!reactRoots.has(targetContainer)) {\n const root = createRoot(rootElement);\n reactRoots.set(targetContainer, root);\n root.render(<StackRenderer container={targetContainer} />);\n }\n\n const state = getContainerState(targetContainer);\n const id = props.id || uuid();\n state.stack.push({ ...props, id });\n state.update?.();\n\n return id;\n };\n\n const destroy = (id?: string) => {\n if (!id) {\n containerStates.forEach(state => {\n state.stack = [];\n state.update?.();\n });\n reactRoots.forEach((root, container) => {\n root.unmount();\n rootElements.get(container)?.remove();\n rootElements.delete(container);\n });\n reactRoots.clear();\n containerStates.clear();\n return;\n }\n\n containerStates.forEach((state, container) => {\n if (!state.stack.some(item => item.id === id)) {\n return;\n }\n\n state.stack = state.stack.filter(item => item.id !== id);\n state.update?.();\n\n if (!state.stack.length) {\n reactRoots.get(container)?.unmount();\n reactRoots.delete(container);\n containerStates.delete(container);\n rootElements.get(container)?.remove();\n rootElements.delete(container);\n }\n });\n };\n\n return { render, destroy, getRootElement };\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,YACX,OAAO,WAAW,eAAe,OAAO,aAAa;AA2BvD,SAAgB,sBAEd,EACA,mBACA,aAC0D;CAC1D,MAAM,kCAAkB,IAAI,KAA0C;CACtE,MAAM,+BAAe,IAAI,SAAmC;CAC5D,MAAM,6BAAa,IAAI,KAAwB;CAE/C,MAAM,qBACJ,cAC2B;EAC3B,IAAI,QAAQ,gBAAgB,IAAI,UAAU;AAE1C,MAAI,CAAC,OAAO;AACV,WAAQ;IAAE,OAAO,EAAE;IAAE,QAAQ;IAAM;AACnC,mBAAgB,IAAI,WAAW,MAAM;;AAGvC,SAAO;;CAGT,MAAM,kBAAkB,cAAgD;AACtE,MAAI,CAAC,UACH,QAAO;EAGT,MAAM,kBAAkB,aAAa,SAAS;EAC9C,IAAI,SAAS,aAAa,IAAI,gBAAgB;AAE9C,MAAI,CAAC,QAAQ;AACX,YAAS,mBAAmB;AAC5B,mBAAgB,YAAY,OAAO;AACnC,gBAAa,IAAI,iBAAiB,OAAO;;AAG3C,SAAO;;CAGT,MAAM,iBAAiB,EAAE,gBAA4C;EACnE,MAAM,GAAG,eAAe,SAAS,EAAE,CAAC;AAEpC,kBAAgB;GACd,MAAM,QAAQ,kBAAkB,UAAU;AAC1C,SAAM,eAAe,YAAY,EAAE,CAAC;AACpC,gBAAa;AACX,UAAM,SAAS;;KAEhB,CAAC,UAAU,CAAC;EAEf,MAAM,EAAE,UAAU,kBAAkB,UAAU;AAE9C,SACE,4CACG,MAAM,KAAK,EAAE,IAAI,GAAG,YACnB,oBAAC;GAAuB;GAAI,GAAK;KAAjB,GAAqC,CACrD,GACD;;CAIP,MAAM,UAAU,UAAsC;AACpD,MAAI,CAAC,UACH;EAGF,MAAM,kBAAkB,MAAM,aAAa,SAAS;EACpD,MAAM,cAAc,eAAe,gBAAgB;AAEnD,MAAI,CAAC,WAAW,IAAI,gBAAgB,EAAE;GACpC,MAAM,OAAO,WAAW,YAAY;AACpC,cAAW,IAAI,iBAAiB,KAAK;AACrC,QAAK,OAAO,oBAAC,iBAAc,WAAW,kBAAmB,CAAC;;EAG5D,MAAM,QAAQ,kBAAkB,gBAAgB;EAChD,MAAM,KAAK,MAAM,MAAMA,IAAM;AAC7B,QAAM,MAAM,KAAK;GAAE,GAAG;GAAO;GAAI,CAAC;AAClC,QAAM,UAAU;AAEhB,SAAO;;CAGT,MAAM,WAAW,OAAgB;AAC/B,MAAI,CAAC,IAAI;AACP,mBAAgB,SAAQ,UAAS;AAC/B,UAAM,QAAQ,EAAE;AAChB,UAAM,UAAU;KAChB;AACF,cAAW,SAAS,MAAM,cAAc;AACtC,SAAK,SAAS;AACd,iBAAa,IAAI,UAAU,EAAE,QAAQ;AACrC,iBAAa,OAAO,UAAU;KAC9B;AACF,cAAW,OAAO;AAClB,mBAAgB,OAAO;AACvB;;AAGF,kBAAgB,SAAS,OAAO,cAAc;AAC5C,OAAI,CAAC,MAAM,MAAM,MAAK,SAAQ,KAAK,OAAO,GAAG,CAC3C;AAGF,SAAM,QAAQ,MAAM,MAAM,QAAO,SAAQ,KAAK,OAAO,GAAG;AACxD,SAAM,UAAU;AAEhB,OAAI,CAAC,MAAM,MAAM,QAAQ;AACvB,eAAW,IAAI,UAAU,EAAE,SAAS;AACpC,eAAW,OAAO,UAAU;AAC5B,oBAAgB,OAAO,UAAU;AACjC,iBAAa,IAAI,UAAU,EAAE,QAAQ;AACrC,iBAAa,OAAO,UAAU;;IAEhC;;AAGJ,QAAO;EAAE;EAAQ;EAAS;EAAgB"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Drawer, Props } from "./drawer.mjs";
|
|
2
|
+
import { Props as Props$1 } from "./list/list.mjs";
|
|
2
3
|
import { List } from "./list/index.mjs";
|
|
3
|
-
import { Modal } from "./modal.mjs";
|
|
4
|
+
import { Modal, Props as Props$2 } from "./modal.mjs";
|
|
5
|
+
import { Props as Props$3 } from "./swiper/swiper.mjs";
|
|
4
6
|
import { Swiper } from "./swiper/index.mjs";
|
|
5
7
|
import { StaticProps, Toast } from "./toast.mjs";
|