@academix-admin/selection-viewer 0.1.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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.cjs +575 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +105 -0
- package/dist/index.d.ts +105 -0
- package/dist/index.js +565 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
3
|
+
import { Sheet } from '@academix-admin/modal-sheet';
|
|
4
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/SelectionViewer.tsx
|
|
7
|
+
var getStyles = (id) => `
|
|
8
|
+
#${id} .selection-viewer-header {
|
|
9
|
+
padding: 0px;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
align-items: center;
|
|
13
|
+
position: relative;
|
|
14
|
+
width: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#${id} .selection-viewer-drag-handle {
|
|
18
|
+
height: 5px;
|
|
19
|
+
border-radius: 3px;
|
|
20
|
+
margin-top: 16px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#${id} .selection-viewer-container {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#${id} .selection-viewer-title {
|
|
30
|
+
margin: 0px;
|
|
31
|
+
font-size: 18px;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
padding: 0px 16px 0px 16px;
|
|
34
|
+
word-break: break-word; /* break long words */
|
|
35
|
+
white-space: normal; /* allow wrapping */
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#${id} .selection-viewer-search {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
margin: 16px 16px 4px 16px;
|
|
42
|
+
border-radius: 12px;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#${id} .selection-viewer-search-input {
|
|
48
|
+
flex: 1;
|
|
49
|
+
border: none;
|
|
50
|
+
background: transparent;
|
|
51
|
+
padding: 12px 8px;
|
|
52
|
+
outline: none;
|
|
53
|
+
font-size: 16px !important;
|
|
54
|
+
width: 100%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#${id} .selection-viewer-content {
|
|
58
|
+
overflow-y: auto;
|
|
59
|
+
-webkit-overflow-scrolling: touch;
|
|
60
|
+
flex: 1;
|
|
61
|
+
min-height: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#${id} .selection-viewer-content.vertical {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
gap: 8px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#${id} .selection-viewer-content.horizontal {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: row;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
overflow-x: auto;
|
|
75
|
+
overflow-y: hidden;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#${id} .selection-viewer-no-results,
|
|
79
|
+
#${id} .selection-viewer-error,
|
|
80
|
+
#${id} .selection-viewer-loading {
|
|
81
|
+
display: flex;
|
|
82
|
+
justify-content: center;
|
|
83
|
+
align-items: center;
|
|
84
|
+
min-height: 120px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#${id} .selection-viewer-default-no-results {
|
|
88
|
+
color: #666;
|
|
89
|
+
font-size: 14px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#${id} .selection-viewer-default-error {
|
|
93
|
+
color: red;
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Full screen search mode */
|
|
98
|
+
#${id} .selection-viewer-fullscreen-search {
|
|
99
|
+
// position: fixed;
|
|
100
|
+
top: 0;
|
|
101
|
+
left: 0;
|
|
102
|
+
right: 0;
|
|
103
|
+
z-index: 10;
|
|
104
|
+
// padding-top: env(safe-area-inset-top);
|
|
105
|
+
width: 100%;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#${id} .selection-viewer-search-back-button {
|
|
109
|
+
background: none;
|
|
110
|
+
border: none;
|
|
111
|
+
padding: 8px;
|
|
112
|
+
margin-right: 4px;
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
transition: opacity 0.2s;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#${id} .selection-viewer-search-back-button:hover {
|
|
121
|
+
opacity: 0.7;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#${id} .selection-viewer-search-clear-button {
|
|
125
|
+
background: none;
|
|
126
|
+
border: none;
|
|
127
|
+
padding: 8px;
|
|
128
|
+
margin-left: 4px;
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
justify-content: center;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
transition: opacity 0.2s;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#${id} .selection-viewer-search-clear-button:hover {
|
|
137
|
+
opacity: 0.7;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* React Modal Sheet overrides */
|
|
141
|
+
#${id} .react-modal-sheet-container {
|
|
142
|
+
max-width: 500px;
|
|
143
|
+
margin: 0 auto;
|
|
144
|
+
border-top-left-radius: 16px;
|
|
145
|
+
border-top-right-radius: 16px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#${id} .react-modal-sheet-backdrop {
|
|
149
|
+
background-color: rgba(0, 0, 0, 0.5) !important;
|
|
150
|
+
pointer-events: auto !important;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#${id} .react-modal-sheet-content {
|
|
154
|
+
padding: 0 0px 0px 0px;
|
|
155
|
+
height: 100%;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/* Mobile full-width behavior */
|
|
160
|
+
@media (max-width: 500px) {
|
|
161
|
+
#${id} .react-modal-sheet-container {
|
|
162
|
+
max-width: 100%;
|
|
163
|
+
border-radius: 0;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Prevent iOS zooming */
|
|
168
|
+
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
|
169
|
+
#${id} .selection-viewer-search-input {
|
|
170
|
+
font-size: 16px !important;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
var useInjectStyles = (id) => {
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
const styleId = `selection-viewer-styles-${id}`;
|
|
177
|
+
let styleTag = document.getElementById(styleId);
|
|
178
|
+
if (!styleTag) {
|
|
179
|
+
styleTag = document.createElement("style");
|
|
180
|
+
styleTag.id = styleId;
|
|
181
|
+
styleTag.innerHTML = getStyles(id);
|
|
182
|
+
document.head.appendChild(styleTag);
|
|
183
|
+
}
|
|
184
|
+
return () => {
|
|
185
|
+
if (styleTag && document.head.contains(styleTag)) {
|
|
186
|
+
document.head.removeChild(styleTag);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}, [id]);
|
|
190
|
+
};
|
|
191
|
+
var SelectionViewer = ({
|
|
192
|
+
id: providedId,
|
|
193
|
+
isOpen,
|
|
194
|
+
backDrop = true,
|
|
195
|
+
onClose,
|
|
196
|
+
titleProp,
|
|
197
|
+
searchProp,
|
|
198
|
+
loadingProp,
|
|
199
|
+
noResultProp,
|
|
200
|
+
errorProp,
|
|
201
|
+
cancelButton,
|
|
202
|
+
layoutProp,
|
|
203
|
+
childrenDirection = "vertical",
|
|
204
|
+
children,
|
|
205
|
+
onPaginate,
|
|
206
|
+
snapPoints = [0, 1],
|
|
207
|
+
initialSnap = 1,
|
|
208
|
+
unmountOnClose = true,
|
|
209
|
+
zIndex = 1e3,
|
|
210
|
+
maxHeight = "90dvh",
|
|
211
|
+
minHeight = "65dvh",
|
|
212
|
+
selectionState = "initial",
|
|
213
|
+
closeThreshold = 0.2
|
|
214
|
+
}) => {
|
|
215
|
+
const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);
|
|
216
|
+
const [searchValue, setSearchValue] = useState("");
|
|
217
|
+
const [isSearchFocused, setIsSearchFocused] = useState(false);
|
|
218
|
+
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
219
|
+
const [inputKey, setInputKey] = useState(0);
|
|
220
|
+
const [shouldAutoFocus, setShouldAutoFocus] = useState(false);
|
|
221
|
+
const isPaginating = useRef(false);
|
|
222
|
+
const searchInputRef = useRef(null);
|
|
223
|
+
const sheetRef = useRef(null);
|
|
224
|
+
useRef(null);
|
|
225
|
+
useInjectStyles(id);
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (typeof window === "undefined" || !window.visualViewport) return;
|
|
228
|
+
const handleResize = () => {
|
|
229
|
+
const vp = window.visualViewport;
|
|
230
|
+
const kbHeight = window.innerHeight - vp.height;
|
|
231
|
+
setKeyboardHeight(kbHeight > 0 ? kbHeight : 0);
|
|
232
|
+
};
|
|
233
|
+
window.visualViewport.addEventListener("resize", handleResize);
|
|
234
|
+
window.visualViewport.addEventListener("scroll", handleResize);
|
|
235
|
+
return () => {
|
|
236
|
+
window.visualViewport?.removeEventListener("resize", handleResize);
|
|
237
|
+
window.visualViewport?.removeEventListener("scroll", handleResize);
|
|
238
|
+
};
|
|
239
|
+
}, []);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (!isOpen) {
|
|
242
|
+
searchInputRef.current?.blur();
|
|
243
|
+
setSearchValue("");
|
|
244
|
+
searchProp?.onChange?.("");
|
|
245
|
+
setShouldAutoFocus(false);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
}, [isOpen, searchProp?.autoFocus]);
|
|
249
|
+
const handleOpenEnd = useCallback(() => {
|
|
250
|
+
if (searchProp?.autoFocus && searchInputRef.current) {
|
|
251
|
+
setInputKey((prev) => prev + 1);
|
|
252
|
+
setShouldAutoFocus(true);
|
|
253
|
+
searchInputRef.current.focus();
|
|
254
|
+
searchInputRef.current.click();
|
|
255
|
+
}
|
|
256
|
+
}, [searchProp?.autoFocus]);
|
|
257
|
+
const handleSearchChange = (e) => {
|
|
258
|
+
const value = e.target.value;
|
|
259
|
+
setSearchValue(value);
|
|
260
|
+
searchProp?.onChange?.(value);
|
|
261
|
+
};
|
|
262
|
+
const handleSearchFocus = () => {
|
|
263
|
+
setIsSearchFocused(true);
|
|
264
|
+
searchProp?.onFocus?.();
|
|
265
|
+
requestAnimationFrame(() => {
|
|
266
|
+
searchInputRef.current?.focus();
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
const handleSearchBlur = () => {
|
|
270
|
+
searchProp?.onBlur?.();
|
|
271
|
+
};
|
|
272
|
+
const handleBackFromSearch = () => {
|
|
273
|
+
setIsSearchFocused(false);
|
|
274
|
+
if (searchInputRef.current) {
|
|
275
|
+
searchInputRef.current.blur();
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const clearSearch = () => {
|
|
279
|
+
setSearchValue("");
|
|
280
|
+
searchProp?.onChange?.("");
|
|
281
|
+
if (searchInputRef.current) {
|
|
282
|
+
searchInputRef.current.focus();
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const handleScroll = useCallback(
|
|
286
|
+
async (e) => {
|
|
287
|
+
if (isPaginating.current || !onPaginate) return;
|
|
288
|
+
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
|
289
|
+
const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;
|
|
290
|
+
if (isNearBottom) {
|
|
291
|
+
isPaginating.current = true;
|
|
292
|
+
const hasMore = await onPaginate();
|
|
293
|
+
if (!hasMore) {
|
|
294
|
+
isPaginating.current = false;
|
|
295
|
+
} else {
|
|
296
|
+
setTimeout(() => {
|
|
297
|
+
isPaginating.current = false;
|
|
298
|
+
}, 500);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
[onPaginate]
|
|
303
|
+
);
|
|
304
|
+
if (!isOpen && unmountOnClose) return null;
|
|
305
|
+
return /* @__PURE__ */ jsxs(
|
|
306
|
+
Sheet,
|
|
307
|
+
{
|
|
308
|
+
ref: sheetRef,
|
|
309
|
+
isOpen,
|
|
310
|
+
onClose,
|
|
311
|
+
detent: "content",
|
|
312
|
+
style: { zIndex },
|
|
313
|
+
maxHeight,
|
|
314
|
+
onOpenEnd: handleOpenEnd,
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ jsxs(
|
|
317
|
+
Sheet.Container,
|
|
318
|
+
{
|
|
319
|
+
id,
|
|
320
|
+
style: {
|
|
321
|
+
maxHeight,
|
|
322
|
+
minHeight: isSearchFocused ? "100dvh" : minHeight,
|
|
323
|
+
maxWidth: layoutProp?.maxWidth ?? "800px",
|
|
324
|
+
margin: "0 auto",
|
|
325
|
+
width: "100%",
|
|
326
|
+
left: 0,
|
|
327
|
+
right: 0,
|
|
328
|
+
paddingBottom: "calc(0px + env(safe-area-inset-bottom))",
|
|
329
|
+
background: isSearchFocused ? layoutProp?.fullScreenSearchBackground || layoutProp?.backgroundColor : layoutProp?.backgroundColor,
|
|
330
|
+
borderTopLeftRadius: isSearchFocused ? "0px" : "16px",
|
|
331
|
+
borderTopRightRadius: isSearchFocused ? "0px" : "16px"
|
|
332
|
+
},
|
|
333
|
+
children: [
|
|
334
|
+
isSearchFocused ? /* @__PURE__ */ jsx(Sheet.Header, { children: /* @__PURE__ */ jsx(
|
|
335
|
+
"div",
|
|
336
|
+
{
|
|
337
|
+
className: "selection-viewer-fullscreen-search",
|
|
338
|
+
style: layoutProp?.fullScreenSearchHeaderStyle,
|
|
339
|
+
children: /* @__PURE__ */ jsxs("div", { className: "selection-viewer-search", style: {
|
|
340
|
+
...searchProp?.containerStyle,
|
|
341
|
+
background: searchProp?.background,
|
|
342
|
+
padding: searchProp?.padding ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}` : "16px"
|
|
343
|
+
}, children: [
|
|
344
|
+
/* @__PURE__ */ jsx(
|
|
345
|
+
"button",
|
|
346
|
+
{
|
|
347
|
+
className: "selection-viewer-search-back-button",
|
|
348
|
+
onClick: handleBackFromSearch,
|
|
349
|
+
"aria-label": "Exit search mode",
|
|
350
|
+
children: searchProp?.backIcon || /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M15 18L9 12L15 6", stroke: searchProp?.textColor || "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
351
|
+
}
|
|
352
|
+
),
|
|
353
|
+
/* @__PURE__ */ jsx(
|
|
354
|
+
"input",
|
|
355
|
+
{
|
|
356
|
+
ref: searchInputRef,
|
|
357
|
+
type: "text",
|
|
358
|
+
placeholder: searchProp?.text,
|
|
359
|
+
value: searchValue,
|
|
360
|
+
onChange: handleSearchChange,
|
|
361
|
+
className: searchProp?.className || "selection-viewer-search-input",
|
|
362
|
+
style: {
|
|
363
|
+
color: searchProp?.textColor,
|
|
364
|
+
...searchProp?.inputStyle
|
|
365
|
+
},
|
|
366
|
+
onFocus: handleSearchFocus,
|
|
367
|
+
onBlur: handleSearchBlur,
|
|
368
|
+
autoFocus: shouldAutoFocus
|
|
369
|
+
},
|
|
370
|
+
inputKey
|
|
371
|
+
),
|
|
372
|
+
searchValue && /* @__PURE__ */ jsx(
|
|
373
|
+
"button",
|
|
374
|
+
{
|
|
375
|
+
className: "selection-viewer-search-clear-button",
|
|
376
|
+
onClick: clearSearch,
|
|
377
|
+
"aria-label": "Clear search",
|
|
378
|
+
children: searchProp?.clearIcon || /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: searchProp?.textColor || "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
] })
|
|
382
|
+
}
|
|
383
|
+
) }) : /* @__PURE__ */ jsxs(Sheet.Header, { children: [
|
|
384
|
+
/* @__PURE__ */ jsxs("div", { className: "selection-viewer-header", children: [
|
|
385
|
+
cancelButton?.position === "left" && /* @__PURE__ */ jsx(
|
|
386
|
+
"button",
|
|
387
|
+
{
|
|
388
|
+
style: {
|
|
389
|
+
position: "absolute",
|
|
390
|
+
left: "0px",
|
|
391
|
+
top: "8px",
|
|
392
|
+
border: "none",
|
|
393
|
+
backgroundColor: "transparent",
|
|
394
|
+
...cancelButton.style
|
|
395
|
+
},
|
|
396
|
+
onClick: cancelButton.onClick || onClose,
|
|
397
|
+
children: cancelButton.view || cancelButton.text || "Cancel"
|
|
398
|
+
}
|
|
399
|
+
),
|
|
400
|
+
!isSearchFocused && /* @__PURE__ */ jsx(
|
|
401
|
+
"div",
|
|
402
|
+
{
|
|
403
|
+
className: "selection-viewer-drag-handle",
|
|
404
|
+
style: {
|
|
405
|
+
background: layoutProp?.handleColor || "#ccc",
|
|
406
|
+
width: layoutProp?.handleWidth || "40px",
|
|
407
|
+
marginBottom: layoutProp?.gapBetweenHandleAndTitle || "12px"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
cancelButton?.position === "right" && /* @__PURE__ */ jsx(
|
|
412
|
+
"button",
|
|
413
|
+
{
|
|
414
|
+
style: {
|
|
415
|
+
position: "absolute",
|
|
416
|
+
right: "0px",
|
|
417
|
+
top: "8px",
|
|
418
|
+
border: "none",
|
|
419
|
+
backgroundColor: "transparent",
|
|
420
|
+
...cancelButton.style
|
|
421
|
+
},
|
|
422
|
+
onClick: cancelButton.onClick || onClose,
|
|
423
|
+
children: cancelButton.view || cancelButton.text || "Cancel"
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
/* @__PURE__ */ jsx("div", { className: titleProp.containerClass || "selection-viewer-container", children: /* @__PURE__ */ jsx(
|
|
427
|
+
"h2",
|
|
428
|
+
{
|
|
429
|
+
id: `${id}-title`,
|
|
430
|
+
className: titleProp.className || "selection-viewer-title",
|
|
431
|
+
style: {
|
|
432
|
+
marginBottom: layoutProp?.gapBetweenTitleAndSearch || "8px",
|
|
433
|
+
color: titleProp?.textColor || "black",
|
|
434
|
+
...titleProp.style
|
|
435
|
+
},
|
|
436
|
+
children: titleProp.text
|
|
437
|
+
}
|
|
438
|
+
) })
|
|
439
|
+
] }),
|
|
440
|
+
searchProp && /* @__PURE__ */ jsxs(
|
|
441
|
+
"div",
|
|
442
|
+
{
|
|
443
|
+
className: "selection-viewer-search",
|
|
444
|
+
style: {
|
|
445
|
+
background: searchProp.background,
|
|
446
|
+
padding: searchProp.padding ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}` : "8px 16px",
|
|
447
|
+
marginBottom: layoutProp?.gapBetweenSearchAndContent,
|
|
448
|
+
...searchProp.containerStyle
|
|
449
|
+
},
|
|
450
|
+
children: [
|
|
451
|
+
searchProp.prefixIcon && /* @__PURE__ */ jsx("span", { style: { marginRight: searchProp.prefixGap || "8px" }, children: searchProp.prefixIcon }),
|
|
452
|
+
/* @__PURE__ */ jsx(
|
|
453
|
+
"input",
|
|
454
|
+
{
|
|
455
|
+
ref: searchInputRef,
|
|
456
|
+
type: "text",
|
|
457
|
+
placeholder: searchProp.text,
|
|
458
|
+
value: searchValue,
|
|
459
|
+
onChange: handleSearchChange,
|
|
460
|
+
className: searchProp.className || "selection-viewer-search-input",
|
|
461
|
+
style: {
|
|
462
|
+
color: searchProp?.textColor,
|
|
463
|
+
...searchProp.inputStyle
|
|
464
|
+
},
|
|
465
|
+
onFocus: handleSearchFocus,
|
|
466
|
+
onBlur: handleSearchBlur,
|
|
467
|
+
autoFocus: shouldAutoFocus
|
|
468
|
+
},
|
|
469
|
+
inputKey
|
|
470
|
+
),
|
|
471
|
+
searchValue && /* @__PURE__ */ jsx(
|
|
472
|
+
"button",
|
|
473
|
+
{
|
|
474
|
+
className: "selection-viewer-search-clear-button",
|
|
475
|
+
onClick: clearSearch,
|
|
476
|
+
"aria-label": "Clear search",
|
|
477
|
+
style: { marginLeft: searchProp.suffixGap || "8px" },
|
|
478
|
+
children: searchProp.clearIcon || searchProp.suffixIcon || /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
479
|
+
}
|
|
480
|
+
),
|
|
481
|
+
!searchValue && searchProp.suffixIcon && /* @__PURE__ */ jsx("span", { style: { marginLeft: searchProp.suffixGap || "8px" }, children: searchProp.suffixIcon })
|
|
482
|
+
]
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
] }),
|
|
486
|
+
/* @__PURE__ */ jsx(Sheet.Content, { children: /* @__PURE__ */ jsx(
|
|
487
|
+
"div",
|
|
488
|
+
{
|
|
489
|
+
className: `selection-viewer-content ${childrenDirection}`,
|
|
490
|
+
onScroll: onPaginate ? handleScroll : void 0,
|
|
491
|
+
style: {
|
|
492
|
+
paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : "0",
|
|
493
|
+
paddingBottom: keyboardHeight > 0 ? `${keyboardHeight + 16}px` : "16px"
|
|
494
|
+
},
|
|
495
|
+
children: React.Children.count(children) > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
496
|
+
children,
|
|
497
|
+
selectionState === "loading" && /* @__PURE__ */ jsx(
|
|
498
|
+
"div",
|
|
499
|
+
{
|
|
500
|
+
className: "selection-viewer-loading",
|
|
501
|
+
style: {
|
|
502
|
+
padding: loadingProp?.padding ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}` : "16px",
|
|
503
|
+
...loadingProp?.style
|
|
504
|
+
},
|
|
505
|
+
children: loadingProp?.view
|
|
506
|
+
}
|
|
507
|
+
)
|
|
508
|
+
] }) : selectionState === "empty" || React.Children.count(children) <= 0 && (selectionState != "loading" && selectionState != "error") ? /* @__PURE__ */ jsx(
|
|
509
|
+
"div",
|
|
510
|
+
{
|
|
511
|
+
className: "selection-viewer-no-results",
|
|
512
|
+
style: {
|
|
513
|
+
padding: noResultProp?.padding ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}` : "16px",
|
|
514
|
+
...noResultProp?.style
|
|
515
|
+
},
|
|
516
|
+
children: noResultProp?.view || /* @__PURE__ */ jsx("div", { className: "selection-viewer-default-no-results", children: noResultProp?.text || "No results found" })
|
|
517
|
+
}
|
|
518
|
+
) : selectionState === "loading" ? /* @__PURE__ */ jsx(
|
|
519
|
+
"div",
|
|
520
|
+
{
|
|
521
|
+
className: "selection-viewer-loading",
|
|
522
|
+
style: {
|
|
523
|
+
padding: loadingProp?.padding ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}` : "16px",
|
|
524
|
+
...loadingProp?.style
|
|
525
|
+
},
|
|
526
|
+
children: loadingProp?.view
|
|
527
|
+
}
|
|
528
|
+
) : selectionState === "error" ? /* @__PURE__ */ jsx(
|
|
529
|
+
"div",
|
|
530
|
+
{
|
|
531
|
+
className: "selection-viewer-error",
|
|
532
|
+
style: {
|
|
533
|
+
padding: errorProp?.padding ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}` : "16px",
|
|
534
|
+
...errorProp?.style
|
|
535
|
+
},
|
|
536
|
+
children: errorProp?.view || /* @__PURE__ */ jsx("div", { className: "selection-viewer-default-error", children: errorProp?.text || "No results found" })
|
|
537
|
+
}
|
|
538
|
+
) : null
|
|
539
|
+
}
|
|
540
|
+
) })
|
|
541
|
+
]
|
|
542
|
+
}
|
|
543
|
+
),
|
|
544
|
+
/* @__PURE__ */ jsx(Sheet.Backdrop, { onTap: backDrop ? onClose : void 0 })
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
};
|
|
549
|
+
var useSelectionController = (initialSelectionState) => {
|
|
550
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
551
|
+
const [selectionState, setSelectionState] = useState(initialSelectionState || "initial");
|
|
552
|
+
const [selectionId] = useState(() => `selection-${Math.random().toString(36).substring(2, 11)}`);
|
|
553
|
+
const operations = useMemo(() => ({
|
|
554
|
+
open: () => setIsOpen(true),
|
|
555
|
+
close: () => setIsOpen(false),
|
|
556
|
+
toggle: () => setIsOpen((prev) => !prev),
|
|
557
|
+
setSelectionState
|
|
558
|
+
}), [setSelectionState]);
|
|
559
|
+
return [selectionId, operations, isOpen, selectionState];
|
|
560
|
+
};
|
|
561
|
+
var SelectionViewer_default = SelectionViewer;
|
|
562
|
+
|
|
563
|
+
export { SelectionViewer_default as SelectionViewer, SelectionViewer_default as default, useSelectionController };
|
|
564
|
+
//# sourceMappingURL=index.js.map
|
|
565
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SelectionViewer.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import React, { useState, useEffect, useCallback, useRef, useMemo } from \"react\";\nimport { Sheet } from \"@academix-admin/modal-sheet\";\n\n// ==================== Types ====================\ntype Padding = {\n l: string;\n r: string;\n t: string;\n b: string;\n};\n\ntype SnapPoint = number;\n\ntype SelectionState = \"loading\" | \"empty\" | \"error\" | \"data\" | \"initial\";\n\n\ntype TitleProps = {\n text: string;\n textColor: string;\n className?: string;\n containerClass?: string;\n style?: React.CSSProperties;\n};\n\ntype SearchProps = {\n text: string;\n textColor: string;\n className?: string;\n background?: string;\n prefixIcon?: React.ReactNode;\n suffixIcon?: React.ReactNode;\n clearIcon?: React.ReactNode;\n backIcon?: React.ReactNode;\n prefixGap?: string;\n suffixGap?: string;\n padding?: Padding;\n autoFocus?: boolean;\n inputStyle?: React.CSSProperties;\n containerStyle?: React.CSSProperties;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n};\n\ntype LoadingProps = {\n view: React.ReactNode;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype NoResultProps = {\n view: React.ReactNode;\n text?: string;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype ErrorProps = {\n view: React.ReactNode;\n text?: string;\n padding?: Padding;\n style?: React.CSSProperties;\n};\n\ntype CancelButtonProps = {\n text?: string;\n view: React.ReactNode;\n position?: \"left\" | \"right\";\n style?: React.CSSProperties;\n onClick?: () => void;\n};\n\ntype LayoutProps = {\n gapBetweenHandleAndTitle?: string;\n gapBetweenTitleAndSearch?: string;\n gapBetweenSearchAndContent?: string;\n backgroundColor?: string;\n handleColor?: string;\n handleWidth?: string;\n fullScreenSearchBackground?: string;\n fullScreenSearchHeaderStyle?: React.CSSProperties;\n maxWidth?: string;\n};\n\ntype SelectionViewerProps = {\n id?: string;\n isOpen: boolean;\n backDrop?: boolean;\n onClose: () => void;\n titleProp: TitleProps;\n searchProp?: SearchProps;\n loadingProp?: LoadingProps;\n noResultProp?: NoResultProps;\n errorProp?: ErrorProps;\n cancelButton?: CancelButtonProps;\n layoutProp?: LayoutProps;\n childrenDirection?: \"vertical\" | \"horizontal\";\n children?: React.ReactNode;\n onPaginate?: () => boolean | Promise<boolean>;\n snapPoints?: SnapPoint[];\n initialSnap?: number;\n unmountOnClose?: boolean;\n zIndex?: number;\n maxHeight?: string;\n minHeight?: string;\n selectionState?: SelectionState;\n closeThreshold?: number;\n};\n\n// ==================== Styles ====================\nconst getStyles = (id: string) => `\n#${id} .selection-viewer-header {\n padding: 0px;\n display: flex;\n flex-direction: column;\n align-items: center;\n position: relative;\n width: 100%;\n}\n\n#${id} .selection-viewer-drag-handle {\n height: 5px;\n border-radius: 3px;\n margin-top: 16px;\n}\n\n#${id} .selection-viewer-container {\n display: flex;\n align-items: flex-start;\n width: 100%;\n}\n\n#${id} .selection-viewer-title {\n margin: 0px;\n font-size: 18px;\n font-weight: 600;\n padding: 0px 16px 0px 16px;\n word-break: break-word; /* break long words */\n white-space: normal; /* allow wrapping */\n}\n\n#${id} .selection-viewer-search {\n display: flex;\n align-items: center;\n margin: 16px 16px 4px 16px;\n border-radius: 12px;\n box-sizing: border-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n#${id} .selection-viewer-search-input {\n flex: 1;\n border: none;\n background: transparent;\n padding: 12px 8px;\n outline: none;\n font-size: 16px !important;\n width: 100%;\n}\n\n#${id} .selection-viewer-content {\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n flex: 1;\n min-height: 0;\n}\n\n#${id} .selection-viewer-content.vertical {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n#${id} .selection-viewer-content.horizontal {\n display: flex;\n flex-direction: row;\n gap: 8px;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n#${id} .selection-viewer-no-results,\n#${id} .selection-viewer-error,\n#${id} .selection-viewer-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 120px;\n}\n\n#${id} .selection-viewer-default-no-results {\n color: #666;\n font-size: 14px;\n}\n\n#${id} .selection-viewer-default-error {\n color: red;\n font-size: 14px;\n}\n\n/* Full screen search mode */\n#${id} .selection-viewer-fullscreen-search {\n// position: fixed;\n top: 0;\n left: 0;\n right: 0;\n z-index: 10;\n// padding-top: env(safe-area-inset-top);\n width: 100%;\n}\n\n#${id} .selection-viewer-search-back-button {\n background: none;\n border: none;\n padding: 8px;\n margin-right: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n#${id} .selection-viewer-search-back-button:hover {\n opacity: 0.7;\n}\n\n#${id} .selection-viewer-search-clear-button {\n background: none;\n border: none;\n padding: 8px;\n margin-left: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n#${id} .selection-viewer-search-clear-button:hover {\n opacity: 0.7;\n}\n\n/* React Modal Sheet overrides */\n#${id} .react-modal-sheet-container {\n max-width: 500px;\n margin: 0 auto;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n}\n\n#${id} .react-modal-sheet-backdrop {\n background-color: rgba(0, 0, 0, 0.5) !important;\n pointer-events: auto !important;\n}\n\n#${id} .react-modal-sheet-content {\n padding: 0 0px 0px 0px;\n height: 100%;\n}\n\n\n/* Mobile full-width behavior */\n@media (max-width: 500px) {\n #${id} .react-modal-sheet-container {\n max-width: 100%;\n border-radius: 0;\n }\n}\n\n/* Prevent iOS zooming */\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n #${id} .selection-viewer-search-input {\n font-size: 16px !important;\n }\n}\n`;\n\n// ==================== Hook to inject CSS per instance ====================\nconst useInjectStyles = (id: string) => {\n useEffect(() => {\n const styleId = `selection-viewer-styles-${id}`;\n let styleTag = document.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleTag) {\n styleTag = document.createElement(\"style\");\n styleTag.id = styleId;\n styleTag.innerHTML = getStyles(id);\n document.head.appendChild(styleTag);\n }\n\n // Cleanup on unmount\n return () => {\n if (styleTag && document.head.contains(styleTag)) {\n document.head.removeChild(styleTag);\n }\n };\n }, [id]);\n};\n\n// ==================== Component ====================\nconst SelectionViewer: React.FC<SelectionViewerProps> = ({\n id: providedId,\n isOpen,\n backDrop = true,\n onClose,\n titleProp,\n searchProp,\n loadingProp,\n noResultProp,\n errorProp,\n cancelButton,\n layoutProp,\n childrenDirection = \"vertical\",\n children,\n onPaginate,\n snapPoints = [0, 1],\n initialSnap = 1,\n unmountOnClose = true,\n zIndex = 1000,\n maxHeight = \"90dvh\",\n minHeight = \"65dvh\",\n selectionState = \"initial\",\n closeThreshold = 0.2,\n}) => {\n const [id] = useState(() => providedId || `selection-${Math.random().toString(36).substring(2, 11)}`);\n const [searchValue, setSearchValue] = useState(\"\");\n const [isSearchFocused, setIsSearchFocused] = useState(false);\n const [keyboardHeight, setKeyboardHeight] = useState(0);\n const [inputKey, setInputKey] = useState(0);\n const [shouldAutoFocus, setShouldAutoFocus] = useState(false);\n const isPaginating = useRef(false);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const sheetRef = useRef<any>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n\n useInjectStyles(id);\n\n // Track keyboard height\n useEffect(() => {\n if (typeof window === 'undefined' || !window.visualViewport) return;\n\n const handleResize = () => {\n const vp = window.visualViewport!;\n const kbHeight = window.innerHeight - vp.height;\n setKeyboardHeight(kbHeight > 0 ? kbHeight : 0);\n };\n\n window.visualViewport.addEventListener('resize', handleResize);\n window.visualViewport.addEventListener('scroll', handleResize);\n\n return () => {\n window.visualViewport?.removeEventListener('resize', handleResize);\n window.visualViewport?.removeEventListener('scroll', handleResize);\n };\n }, []);\n\n // Auto-focus on search\n useEffect(() => {\n if (!isOpen) {\n searchInputRef.current?.blur();\n setSearchValue(\"\");\n searchProp?.onChange?.(\"\");\n setShouldAutoFocus(false);\n return;\n }\n }, [isOpen, searchProp?.autoFocus]);\n\n const handleOpenEnd = useCallback(() => {\n if (searchProp?.autoFocus && searchInputRef.current) {\n setInputKey(prev => prev + 1);\n setShouldAutoFocus(true);\n searchInputRef.current.focus();\n searchInputRef.current.click();\n }\n }, [searchProp?.autoFocus]);\n\n const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n setSearchValue(value);\n searchProp?.onChange?.(value);\n };\n\n const handleSearchFocus = () => {\n setIsSearchFocused(true);\n searchProp?.onFocus?.();\n requestAnimationFrame(() => {\n searchInputRef.current?.focus();\n });\n };\n\n const handleSearchBlur = () => {\n searchProp?.onBlur?.();\n };\n\n const handleBackFromSearch = () => {\n setIsSearchFocused(false);\n if (searchInputRef.current) {\n searchInputRef.current.blur();\n }\n };\n\n const clearSearch = () => {\n setSearchValue(\"\");\n searchProp?.onChange?.(\"\");\n if (searchInputRef.current) {\n searchInputRef.current.focus();\n }\n };\n\n const handleScroll = useCallback(\n async (e: React.UIEvent<HTMLDivElement>) => {\n if (isPaginating.current || !onPaginate) return;\n\n const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;\n const isNearBottom = scrollHeight - scrollTop <= clientHeight * 1.2;\n\n if (isNearBottom) {\n isPaginating.current = true;\n const hasMore = await onPaginate();\n if (!hasMore) {\n isPaginating.current = false;\n } else {\n setTimeout(() => {\n isPaginating.current = false;\n }, 500);\n }\n }\n },\n [onPaginate]\n );\n\n if (!isOpen && unmountOnClose) return null;\n\n return (\n <Sheet\n ref={sheetRef}\n isOpen={isOpen}\n onClose={onClose}\n detent=\"content\"\n style={{ zIndex }}\n maxHeight={maxHeight}\n onOpenEnd={handleOpenEnd}\n >\n <Sheet.Container\n id={id}\n style={{\n maxHeight: maxHeight,\n minHeight: isSearchFocused ? \"100dvh\" : minHeight,\n maxWidth: layoutProp?.maxWidth ?? '800px',\n margin: \"0 auto\",\n width: \"100%\",\n left: 0,\n right: 0,\n paddingBottom: \"calc(0px + env(safe-area-inset-bottom))\",\n background: isSearchFocused\n ? layoutProp?.fullScreenSearchBackground || layoutProp?.backgroundColor\n : layoutProp?.backgroundColor,\n borderTopLeftRadius: isSearchFocused ? \"0px\" : \"16px\",\n borderTopRightRadius: isSearchFocused ? \"0px\" : \"16px\",\n }}\n >\n {isSearchFocused ? (\n <Sheet.Header>\n <div\n className=\"selection-viewer-fullscreen-search\"\n style={layoutProp?.fullScreenSearchHeaderStyle}\n >\n <div className=\"selection-viewer-search\" style={{\n ...searchProp?.containerStyle,\n background: searchProp?.background,\n padding: searchProp?.padding\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\n : \"16px\"\n }}>\n <button\n className=\"selection-viewer-search-back-button\"\n onClick={handleBackFromSearch}\n aria-label=\"Exit search mode\"\n >\n {searchProp?.backIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M15 18L9 12L15 6\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n\n <input\n key={inputKey}\n ref={searchInputRef}\n type=\"text\"\n placeholder={searchProp?.text}\n value={searchValue}\n onChange={handleSearchChange}\n className={searchProp?.className || \"selection-viewer-search-input\"}\n style={{\n color: searchProp?.textColor,\n ...searchProp?.inputStyle\n }}\n onFocus={handleSearchFocus}\n onBlur={handleSearchBlur}\n autoFocus={shouldAutoFocus}\n />\n\n {searchValue && (\n <button\n className=\"selection-viewer-search-clear-button\"\n onClick={clearSearch}\n aria-label=\"Clear search\"\n >\n {searchProp?.clearIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke={searchProp?.textColor || 'currentColor'} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n )}\n </div>\n </div>\n </Sheet.Header>\n ) : (\n <Sheet.Header>\n <div className=\"selection-viewer-header\">\n {/* Cancel button left */}\n {cancelButton?.position === \"left\" && (\n <button\n style={{\n position: \"absolute\",\n left: \"0px\",\n top: \"8px\",\n border: \"none\",\n backgroundColor: \"transparent\",\n ...cancelButton.style\n }}\n onClick={cancelButton.onClick || onClose}\n >\n {cancelButton.view || cancelButton.text || \"Cancel\"}\n </button>\n )}\n\n {/* Drag handle */}\n {!isSearchFocused && (\n <div\n className=\"selection-viewer-drag-handle\"\n style={{\n background: layoutProp?.handleColor || \"#ccc\",\n width: layoutProp?.handleWidth || \"40px\",\n marginBottom: layoutProp?.gapBetweenHandleAndTitle || \"12px\",\n }}\n />\n )}\n\n {/* Cancel button right */}\n {cancelButton?.position === \"right\" && (\n <button\n style={{\n position: \"absolute\",\n right: \"0px\",\n top: \"8px\",\n border: \"none\",\n backgroundColor: \"transparent\",\n ...cancelButton.style\n }}\n onClick={cancelButton.onClick || onClose}\n >\n {cancelButton.view || cancelButton.text || \"Cancel\"}\n </button>\n )}\n\n {/* Title */}\n <div className={titleProp.containerClass || \"selection-viewer-container\"}>\n <h2\n id={`${id}-title`}\n className={titleProp.className || \"selection-viewer-title\"}\n style={{\n marginBottom: layoutProp?.gapBetweenTitleAndSearch || \"8px\",\n color: titleProp?.textColor || 'black',\n ...titleProp.style\n }}\n >\n {titleProp.text}\n </h2>\n </div>\n </div>\n\n {/* Search */}\n {searchProp && (\n <div\n className=\"selection-viewer-search\"\n style={{\n background: searchProp.background,\n padding: searchProp.padding\n ? `${searchProp.padding.t} ${searchProp.padding.r} ${searchProp.padding.b} ${searchProp.padding.l}`\n : \"8px 16px\",\n marginBottom: layoutProp?.gapBetweenSearchAndContent,\n ...searchProp.containerStyle\n }}\n >\n {searchProp.prefixIcon && (\n <span style={{ marginRight: searchProp.prefixGap || \"8px\" }}>\n {searchProp.prefixIcon}\n </span>\n )}\n <input\n key={inputKey}\n ref={searchInputRef}\n type=\"text\"\n placeholder={searchProp.text}\n value={searchValue}\n onChange={handleSearchChange}\n className={searchProp.className || \"selection-viewer-search-input\"}\n style={{\n color: searchProp?.textColor,\n ...searchProp.inputStyle\n }}\n onFocus={handleSearchFocus}\n onBlur={handleSearchBlur}\n autoFocus={shouldAutoFocus}\n />\n {searchValue && (\n <button\n className=\"selection-viewer-search-clear-button\"\n onClick={clearSearch}\n aria-label=\"Clear search\"\n style={{ marginLeft: searchProp.suffixGap || \"8px\" }}\n >\n {searchProp.clearIcon || searchProp.suffixIcon || (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )}\n </button>\n )}\n {!searchValue && searchProp.suffixIcon && (\n <span style={{ marginLeft: searchProp.suffixGap || \"8px\" }}>\n {searchProp.suffixIcon}\n </span>\n )}\n </div>\n )}\n </Sheet.Header>\n )}\n\n <Sheet.Content>\n <div\n className={`selection-viewer-content ${childrenDirection}`}\n onScroll={onPaginate ? handleScroll : undefined}\n style={{\n paddingTop: isSearchFocused ? layoutProp?.gapBetweenSearchAndContent : '0',\n paddingBottom: keyboardHeight > 0\n ? `${keyboardHeight + 16}px`\n : '16px',\n }}\n >\n {/* Show children first, then loading at bottom */}\n {React.Children.count(children) > 0 ? (\n <>\n {children}\n {selectionState === \"loading\" && (\n <div\n className=\"selection-viewer-loading\"\n style={{\n padding: loadingProp?.padding\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\n : \"16px\",\n ...loadingProp?.style\n }}\n >\n {loadingProp?.view}\n </div>\n )}\n </>\n ) : (selectionState === \"empty\" || (React.Children.count(children) <= 0 && (selectionState != \"loading\" && selectionState != \"error\"))) ? (\n <div\n className=\"selection-viewer-no-results\"\n style={{\n padding: noResultProp?.padding\n ? `${noResultProp.padding.t} ${noResultProp.padding.r} ${noResultProp.padding.b} ${noResultProp.padding.l}`\n : \"16px\",\n ...noResultProp?.style\n }}\n >\n {noResultProp?.view || (\n <div className=\"selection-viewer-default-no-results\">\n {noResultProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n ) : selectionState === \"loading\" ? (\n <div\n className=\"selection-viewer-loading\"\n style={{\n padding: loadingProp?.padding\n ? `${loadingProp.padding.t} ${loadingProp.padding.r} ${loadingProp.padding.b} ${loadingProp.padding.l}`\n : \"16px\",\n ...loadingProp?.style\n }}\n >\n {loadingProp?.view}\n </div>\n ) : (selectionState === \"error\") ? (\n <div\n className=\"selection-viewer-error\"\n style={{\n padding: errorProp?.padding\n ? `${errorProp.padding.t} ${errorProp.padding.r} ${errorProp.padding.b} ${errorProp.padding.l}`\n : \"16px\",\n ...errorProp?.style\n }}\n >\n {errorProp?.view || (\n <div className=\"selection-viewer-default-error\">\n {errorProp?.text || \"No results found\"}\n </div>\n )}\n </div>\n ) : null}\n </div>\n </Sheet.Content>\n </Sheet.Container>\n <Sheet.Backdrop onTap={backDrop ? onClose : undefined} />\n </Sheet>\n );\n};\n\n// ==================== Controller Hook ====================\ntype Operation = {\n open: () => void;\n close: () => void;\n toggle: () => void;\n setSelectionState: (val: SelectionState) => void;\n};\n\nconst useSelectionController = (initialSelectionState?: SelectionState): [\n string,\n Operation,\n boolean,\n SelectionState\n] => {\n const [isOpen, setIsOpen] = useState(false);\n const [selectionState, setSelectionState] = useState(initialSelectionState || 'initial');\n const [selectionId] = useState(() => `selection-${Math.random().toString(36).substring(2, 11)}`);\n\n const operations = useMemo<Operation>(() => ({\n open: () => setIsOpen(true),\n close: () => setIsOpen(false),\n toggle: () => setIsOpen((prev) => !prev),\n setSelectionState\n }), [setSelectionState]);\n\n return [selectionId, operations, isOpen, selectionState];\n};\n\nexport { SelectionViewer, useSelectionController };\nexport type {\n SelectionViewerProps,\n SelectionState,\n TitleProps,\n SearchProps,\n LoadingProps,\n NoResultProps,\n ErrorProps,\n CancelButtonProps,\n LayoutProps,\n Padding,\n SnapPoint,\n};\nexport default SelectionViewer;\n"]}
|