motile-ui 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,6 +37,19 @@ interface ModalRootProps {
37
37
  * Modal 내용
38
38
  */
39
39
  children: React.ReactNode;
40
+ /**
41
+ * 브라우저 히스토리(뒤로가기 / iOS 스와이프 제스처)로 Modal 닫기 활성화.
42
+ *
43
+ * 옵트인 기능이다. 활성 시 열릴 때 더미 히스토리 항목을 push하고,
44
+ * 브라우저 뒤로가기로 Modal이 닫힌다. 내부적으로 Sheet과 동일한 오버레이
45
+ * 히스토리 레지스트리를 공유하므로 Sheet/Modal 중첩에서도 안전하다.
46
+ *
47
+ * 주의: 열린 상태로 Modal.Root가 언마운트되거나 외부 라우팅(router.push 등)으로
48
+ * 닫히면 정리용 back()이 실행되지 않아 더미 항목이 남을 수 있다(뒤로가기 한 번은 no-op).
49
+ *
50
+ * @default false
51
+ */
52
+ enableHistoryClose?: boolean;
40
53
  }
41
54
  interface ModalOverlayProps extends React.HTMLAttributes<HTMLDivElement> {
42
55
  /**
@@ -1,117 +1,125 @@
1
- import { jsx as l, jsxs as K } from "react/jsx-runtime";
2
- import n, { useRef as L, useState as O, useEffect as P, useId as v, createContext as V, useContext as q } from "react";
3
- import { createPortal as z } from "react-dom";
4
- import { useClickOutside as A } from "../../hooks/useClickOutside.js";
5
- import { useEscapeKey as D } from "../../hooks/useEscapeKey.js";
6
- import { useScrollLock as G } from "../../hooks/useScrollLock.js";
7
- import { Slot as J } from "../../utils/Slot.js";
1
+ import { jsx as l, jsxs as P } from "react/jsx-runtime";
2
+ import n, { useRef as O, useState as V, useEffect as q, useId as _, createContext as z, useContext as A } from "react";
3
+ import { createPortal as D } from "react-dom";
4
+ import { useClickOutside as G } from "../../hooks/useClickOutside.js";
5
+ import { useEscapeKey as J } from "../../hooks/useEscapeKey.js";
6
+ import { useHistoryClose as Q } from "../../hooks/useHistoryClose.js";
7
+ import { useScrollLock as U } from "../../hooks/useScrollLock.js";
8
+ import { Slot as W } from "../../utils/Slot.js";
8
9
  import './Modal.css';/* empty css */
9
- const _ = V(null), m = () => {
10
- const o = q(_);
10
+ const h = z(null), u = () => {
11
+ const o = A(h);
11
12
  if (!o)
12
13
  throw new Error("Modal compound components must be used within Modal.Root");
13
14
  return o;
14
- }, Q = ({
15
+ }, X = ({
15
16
  open: o,
16
17
  onOpenChange: t,
17
- children: e
18
+ children: e,
19
+ enableHistoryClose: a = !1
18
20
  }) => {
19
- const a = v(), d = v(), r = n.useMemo(
21
+ const r = _(), d = _(), { close: s } = Q({
22
+ isOpen: o,
23
+ onClose: () => t(!1),
24
+ enabled: a
25
+ }), i = n.useMemo(
20
26
  () => ({
21
27
  open: o,
22
28
  onOpenChange: t,
23
- titleId: a,
29
+ close: s,
30
+ enableHistoryClose: a,
31
+ titleId: r,
24
32
  descriptionId: d
25
33
  }),
26
- [o, t, a, d]
34
+ [o, t, s, a, r, d]
27
35
  );
28
- return /* @__PURE__ */ l(_.Provider, { value: r, children: e });
36
+ return /* @__PURE__ */ l(h.Provider, { value: i, children: e });
29
37
  }, N = n.forwardRef(
30
38
  ({
31
39
  container: o,
32
40
  variant: t = "scale",
33
41
  closeOnBackdrop: e = !0,
34
42
  disableScrollLock: a = !1,
35
- width: d,
36
- maxWidth: r,
43
+ width: r,
44
+ maxWidth: d,
37
45
  zIndex: s = 1e3,
38
- className: u,
39
- onClick: i,
40
- style: g,
41
- ...p
42
- }, S) => {
43
- const { open: c, onOpenChange: f } = m(), M = L(null), [E, T] = O(!1);
44
- P(() => {
45
- T(!0);
46
+ className: i,
47
+ onClick: c,
48
+ style: S,
49
+ ...y
50
+ }, g) => {
51
+ const { open: m, onOpenChange: E, close: p, enableHistoryClose: T } = u(), M = O(null), [j, k] = V(!1);
52
+ q(() => {
53
+ k(!0);
46
54
  }, []);
47
- const b = typeof e == "boolean" ? e : e.clickOutside ?? !1, h = typeof e == "boolean" ? e : e.escapeKey ?? !1;
48
- G({
49
- enabled: c && !a,
55
+ const b = typeof e == "boolean" ? e : e.clickOutside ?? !1, v = typeof e == "boolean" ? e : e.escapeKey ?? !1;
56
+ U({
57
+ enabled: m && !a,
50
58
  allowedSelectors: [".motile-modal__content"]
51
- }), A({
59
+ }), G({
52
60
  refs: [M],
53
61
  handler: () => {
54
- b && f(!1);
62
+ b && !T && E(!1);
55
63
  },
56
- enabled: c
57
- }), D({
64
+ enabled: m
65
+ }), J({
58
66
  handler: () => {
59
- h && f(!1);
67
+ v && p();
60
68
  },
61
- enabled: c && h,
69
+ enabled: m && v,
62
70
  stacked: !0
63
71
  // 중첩 오버레이에서 ESC는 최상단만 닫는다
64
72
  });
65
- const j = (y) => {
66
- i == null || i(y), y.target === y.currentTarget && b && f(!1);
73
+ const F = (f) => {
74
+ c == null || c(f), f.target === f.currentTarget && b && p();
67
75
  };
68
- if (!E || !c) return null;
69
- const k = {
76
+ if (!j || !m) return null;
77
+ const H = {
70
78
  zIndex: s,
71
- ...g
72
- }, F = {
73
- ...d && { "--modal-width": d },
74
- ...r && { "--modal-max-width": r },
79
+ ...S
80
+ }, K = {
81
+ ...r && { "--modal-width": r },
82
+ ...d && { "--modal-max-width": d },
75
83
  zIndex: s + 1
76
- }, H = /* @__PURE__ */ l(
84
+ }, L = /* @__PURE__ */ l(
77
85
  "div",
78
86
  {
79
- ref: S,
80
- className: `motile-modal__backdrop ${u || ""}`,
81
- "data-state": c ? "open" : "closed",
87
+ ref: g,
88
+ className: `motile-modal__backdrop ${i || ""}`,
89
+ "data-state": m ? "open" : "closed",
82
90
  "data-variant": t,
83
- onClick: j,
84
- style: k,
85
- ...p,
91
+ onClick: F,
92
+ style: H,
93
+ ...y,
86
94
  children: /* @__PURE__ */ l(
87
95
  "div",
88
96
  {
89
97
  ref: M,
90
98
  className: "motile-modal",
91
99
  "data-variant": t,
92
- style: F,
93
- children: p.children
100
+ style: K,
101
+ children: y.children
94
102
  }
95
103
  )
96
104
  }
97
105
  );
98
- return z(H, o || document.body);
106
+ return D(L, o || document.body);
99
107
  }
100
108
  );
101
109
  N.displayName = "Modal.Overlay";
102
110
  const w = n.forwardRef(
103
111
  ({ className: o, forceMount: t, ...e }, a) => {
104
- const { open: d, titleId: r, descriptionId: s } = m();
105
- return !d && !t ? null : /* @__PURE__ */ l(
112
+ const { open: r, titleId: d, descriptionId: s } = u();
113
+ return !r && !t ? null : /* @__PURE__ */ l(
106
114
  "div",
107
115
  {
108
116
  ref: a,
109
117
  className: `motile-modal__content ${o || ""}`,
110
118
  role: "dialog",
111
119
  "aria-modal": "true",
112
- "aria-labelledby": r,
120
+ "aria-labelledby": d,
113
121
  "aria-describedby": s,
114
- "data-state": d ? "open" : "closed",
122
+ "data-state": r ? "open" : "closed",
115
123
  ...e
116
124
  }
117
125
  );
@@ -120,7 +128,7 @@ const w = n.forwardRef(
120
128
  w.displayName = "Modal.Content";
121
129
  const x = n.forwardRef(
122
130
  ({ className: o, ...t }, e) => {
123
- const { titleId: a } = m();
131
+ const { titleId: a } = u();
124
132
  return /* @__PURE__ */ l(
125
133
  "h2",
126
134
  {
@@ -135,7 +143,7 @@ const x = n.forwardRef(
135
143
  x.displayName = "Modal.Title";
136
144
  const R = n.forwardRef(
137
145
  ({ className: o, ...t }, e) => {
138
- const { descriptionId: a } = m();
146
+ const { descriptionId: a } = u();
139
147
  return /* @__PURE__ */ l(
140
148
  "div",
141
149
  {
@@ -149,28 +157,28 @@ const R = n.forwardRef(
149
157
  );
150
158
  R.displayName = "Modal.Body";
151
159
  const C = n.forwardRef(
152
- ({ asChild: o, className: t, onClick: e, children: a, ...d }, r) => {
153
- const { onOpenChange: s } = m(), u = (i) => {
154
- e == null || e(i), s(!1);
160
+ ({ asChild: o, className: t, onClick: e, children: a, ...r }, d) => {
161
+ const { close: s } = u(), i = (c) => {
162
+ e == null || e(c), s();
155
163
  };
156
164
  return o ? /* @__PURE__ */ l(
157
- J,
165
+ W,
158
166
  {
159
- ...d,
160
- onClick: u,
161
- ref: r,
167
+ ...r,
168
+ onClick: i,
169
+ ref: d,
162
170
  children: a
163
171
  }
164
172
  ) : /* @__PURE__ */ l(
165
173
  "button",
166
174
  {
167
- ref: r,
175
+ ref: d,
168
176
  type: "button",
169
177
  className: `motile-modal__close ${t || ""}`,
170
- onClick: u,
178
+ onClick: i,
171
179
  "aria-label": "닫기",
172
- ...d,
173
- children: a || /* @__PURE__ */ K(
180
+ ...r,
181
+ children: a || /* @__PURE__ */ P(
174
182
  "svg",
175
183
  {
176
184
  width: "18",
@@ -214,8 +222,8 @@ const $ = n.forwardRef(
214
222
  )
215
223
  );
216
224
  $.displayName = "Modal.Header";
217
- const te = {
218
- Root: Q,
225
+ const de = {
226
+ Root: X,
219
227
  Overlay: N,
220
228
  Content: w,
221
229
  Title: x,
@@ -225,13 +233,13 @@ const te = {
225
233
  Header: $
226
234
  };
227
235
  export {
228
- te as Modal,
236
+ de as Modal,
229
237
  R as ModalBody,
230
238
  C as ModalClose,
231
239
  w as ModalContent,
232
240
  I as ModalFooter,
233
241
  $ as ModalHeader,
234
242
  N as ModalOverlay,
235
- Q as ModalRoot,
243
+ X as ModalRoot,
236
244
  x as ModalTitle
237
245
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motile-ui",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "description": "A modern React component library for webview applications",
6
6
  "main": "./dist/index.js",