@provex/components 1.3.0-rc.20260419202236.6109d69 → 1.3.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/index.cjs ADDED
@@ -0,0 +1,1249 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var react = require('@chakra-ui/react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var currencies = require('@provex/utils/currencies');
7
+ var react$1 = require('@iconify/react');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var React__default = /*#__PURE__*/_interopDefault(React);
12
+
13
+ // src/Card.tsx
14
+ function Card(props) {
15
+ const { children, bg, ...rest } = props;
16
+ return /* @__PURE__ */ jsxRuntime.jsx(
17
+ react.Box,
18
+ {
19
+ bg: bg ?? "surface.card",
20
+ color: "text.primary",
21
+ boxShadow: "0 0 0 1px var(--chakra-colors-border-default)",
22
+ minW: 0,
23
+ mb: "1px",
24
+ ...rest,
25
+ children
26
+ }
27
+ );
28
+ }
29
+ function CardHorizontalRule(props) {
30
+ const { children, bg, ...rest } = props;
31
+ return /* @__PURE__ */ jsxRuntime.jsx(
32
+ react.Box,
33
+ {
34
+ bg: bg ?? "surface.card",
35
+ color: "text.primary",
36
+ borderTop: "1px solid",
37
+ borderBottom: "1px solid",
38
+ borderColor: "border.default",
39
+ minW: 0,
40
+ ...rest,
41
+ children
42
+ }
43
+ );
44
+ }
45
+ var CurrencyIcon = ({
46
+ currencyId,
47
+ size = 16,
48
+ useZoom = true
49
+ }) => {
50
+ let currency = null;
51
+ try {
52
+ currency = currencies.getCurrencyByContractId(currencyId);
53
+ } catch (err) {
54
+ console.warn("Invalid currency ID:", currencyId, err);
55
+ }
56
+ if (!currency) {
57
+ return /* @__PURE__ */ jsxRuntime.jsx(
58
+ "span",
59
+ {
60
+ style: {
61
+ fontSize: size * 0.75,
62
+ fontWeight: 600,
63
+ color: "#888",
64
+ fontFamily: "monospace"
65
+ },
66
+ title: `Unknown currency: ${currencyId}`,
67
+ children: "?"
68
+ }
69
+ );
70
+ }
71
+ const zoom = useZoom ? currency.zoom ?? 1.5 : 1;
72
+ const objectFit = "cover";
73
+ return /* @__PURE__ */ jsxRuntime.jsx(
74
+ "span",
75
+ {
76
+ style: {
77
+ display: "inline-block",
78
+ width: size,
79
+ height: size,
80
+ overflow: "hidden",
81
+ borderRadius: "100%"
82
+ },
83
+ children: /* @__PURE__ */ jsxRuntime.jsx(
84
+ "img",
85
+ {
86
+ src: currency.flag,
87
+ alt: currency.ticker,
88
+ title: `${currency.name} (${currency.ticker})`,
89
+ style: {
90
+ width: size,
91
+ height: size,
92
+ objectFit,
93
+ transform: `scale(${zoom})`,
94
+ transformOrigin: "center center"
95
+ }
96
+ }
97
+ )
98
+ }
99
+ );
100
+ };
101
+ function OverlappingStack({
102
+ items,
103
+ limit = 3,
104
+ overlap = 8,
105
+ itemSize = 24,
106
+ reverseZIndex = false
107
+ }) {
108
+ const visibleItems = items.slice(0, limit);
109
+ const remainingCount = items.length - limit;
110
+ return /* @__PURE__ */ jsxRuntime.jsxs(
111
+ "div",
112
+ {
113
+ style: {
114
+ display: "flex",
115
+ alignItems: "center"
116
+ },
117
+ children: [
118
+ visibleItems.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
119
+ "div",
120
+ {
121
+ style: {
122
+ marginLeft: index > 0 ? -overlap : 0,
123
+ // Only apply z-index when there's actual overlap (positive overlap value)
124
+ ...overlap > 0 ? { position: "relative", zIndex: reverseZIndex ? index + 1 : limit - index } : {},
125
+ display: "flex",
126
+ alignItems: "center",
127
+ justifyContent: "center",
128
+ minWidth: itemSize,
129
+ minHeight: itemSize
130
+ },
131
+ children: item
132
+ },
133
+ index
134
+ )),
135
+ remainingCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
136
+ "div",
137
+ {
138
+ style: {
139
+ fontSize: 11,
140
+ marginLeft: 4,
141
+ fontWeight: 600,
142
+ opacity: 0.7
143
+ },
144
+ children: [
145
+ "+",
146
+ remainingCount
147
+ ]
148
+ }
149
+ )
150
+ ]
151
+ }
152
+ );
153
+ }
154
+ function DepositIdentifier({
155
+ chainIcon,
156
+ localId,
157
+ itemSize = 20,
158
+ gap = 2,
159
+ textColor
160
+ }) {
161
+ const idString = typeof localId === "bigint" ? localId.toString() : String(localId);
162
+ return /* @__PURE__ */ jsxRuntime.jsx(
163
+ OverlappingStack,
164
+ {
165
+ items: [
166
+ /* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: chainIcon }, "chain"),
167
+ /* @__PURE__ */ jsxRuntime.jsxs(
168
+ "span",
169
+ {
170
+ style: {
171
+ fontSize: "14px",
172
+ fontWeight: 500,
173
+ color: textColor
174
+ },
175
+ children: [
176
+ "#",
177
+ idString
178
+ ]
179
+ },
180
+ "id"
181
+ )
182
+ ],
183
+ overlap: -gap,
184
+ itemSize,
185
+ limit: 2
186
+ }
187
+ );
188
+ }
189
+ var FlagIcon = ({
190
+ flag,
191
+ alt = "Flag",
192
+ size = 32,
193
+ className = "",
194
+ borderColor = "#e5e7eb"
195
+ }) => {
196
+ const handleError = (e) => {
197
+ const target = e.target;
198
+ const canvas = document.createElement("canvas");
199
+ const ctx = canvas.getContext("2d");
200
+ if (ctx) {
201
+ canvas.width = size;
202
+ canvas.height = size;
203
+ ctx.fillStyle = "#e0e0e0";
204
+ ctx.fillRect(0, 0, size, size);
205
+ ctx.fillStyle = "#666";
206
+ ctx.font = `${Math.floor(size * 0.3)}px Arial`;
207
+ ctx.textAlign = "center";
208
+ ctx.textBaseline = "middle";
209
+ ctx.fillText(alt.slice(0, 3), size / 2, size / 2);
210
+ target.src = canvas.toDataURL();
211
+ }
212
+ };
213
+ return /* @__PURE__ */ jsxRuntime.jsx(
214
+ "img",
215
+ {
216
+ src: flag,
217
+ alt,
218
+ className: `inline-block rounded-full ${className}`,
219
+ onError: handleError,
220
+ style: {
221
+ width: `${size}px`,
222
+ height: `${size}px`,
223
+ borderWidth: "1px",
224
+ borderStyle: "solid",
225
+ borderColor,
226
+ minWidth: `${size}px`,
227
+ minHeight: `${size}px`,
228
+ maxWidth: `${size}px`,
229
+ maxHeight: `${size}px`,
230
+ borderRadius: "50%",
231
+ objectFit: "cover",
232
+ objectPosition: "center",
233
+ flexShrink: 0,
234
+ display: "block"
235
+ }
236
+ }
237
+ );
238
+ };
239
+ function Popover({
240
+ isOpen,
241
+ onClose,
242
+ trigger,
243
+ children,
244
+ colors,
245
+ placement = "bottom-start",
246
+ width,
247
+ minWidth = 200,
248
+ fullWidth = false,
249
+ gap = 4
250
+ }) {
251
+ const popoverRef = React.useRef(null);
252
+ const triggerRef = React.useRef(null);
253
+ const [position, setPosition] = React.useState(null);
254
+ const [triggerWidth, setTriggerWidth] = React.useState(void 0);
255
+ React.useEffect(() => {
256
+ if (!isOpen || !triggerRef.current) return;
257
+ const updatePosition = () => {
258
+ if (!triggerRef.current || !popoverRef.current) return;
259
+ const rect = triggerRef.current.getBoundingClientRect();
260
+ if (fullWidth) setTriggerWidth(rect.width);
261
+ const popoverRect = popoverRef.current.getBoundingClientRect();
262
+ const popoverWidth = popoverRect.width || width || (fullWidth ? rect.width : minWidth);
263
+ const popoverHeight = popoverRect.height || 400;
264
+ let top = 0;
265
+ let left = 0;
266
+ if (placement.startsWith("bottom")) {
267
+ top = rect.bottom + gap;
268
+ if (top + popoverHeight > window.innerHeight - 8) {
269
+ top = rect.top - popoverHeight - gap;
270
+ }
271
+ } else {
272
+ top = rect.top - popoverHeight - gap;
273
+ if (top < 8) {
274
+ top = rect.bottom + gap;
275
+ }
276
+ }
277
+ if (placement.endsWith("start")) {
278
+ left = rect.left;
279
+ if (left + popoverWidth > window.innerWidth - 8) {
280
+ left = window.innerWidth - popoverWidth - 8;
281
+ }
282
+ } else {
283
+ left = rect.right - popoverWidth;
284
+ if (left < 8) {
285
+ left = 8;
286
+ }
287
+ }
288
+ left = Math.max(8, Math.min(left, window.innerWidth - popoverWidth - 8));
289
+ top = Math.max(8, Math.min(top, window.innerHeight - popoverHeight - 8));
290
+ setPosition({ top, left });
291
+ };
292
+ const rafId = requestAnimationFrame(() => {
293
+ updatePosition();
294
+ });
295
+ window.addEventListener("scroll", updatePosition, true);
296
+ window.addEventListener("resize", updatePosition);
297
+ return () => {
298
+ cancelAnimationFrame(rafId);
299
+ window.removeEventListener("scroll", updatePosition, true);
300
+ window.removeEventListener("resize", updatePosition);
301
+ };
302
+ }, [isOpen, placement, width, minWidth, fullWidth, gap]);
303
+ React.useEffect(() => {
304
+ if (!isOpen) return;
305
+ const handleClickOutside = (event) => {
306
+ const target = event.target;
307
+ const isOutsidePopover = popoverRef.current && !popoverRef.current.contains(target);
308
+ const isOutsideTrigger = triggerRef.current && !triggerRef.current.contains(target);
309
+ if (isOutsidePopover && isOutsideTrigger) {
310
+ requestAnimationFrame(() => {
311
+ onClose();
312
+ });
313
+ }
314
+ };
315
+ const timeoutId = setTimeout(() => {
316
+ document.addEventListener("click", handleClickOutside, { capture: false });
317
+ }, 50);
318
+ return () => {
319
+ clearTimeout(timeoutId);
320
+ document.removeEventListener("click", handleClickOutside, { capture: false });
321
+ };
322
+ }, [isOpen, onClose]);
323
+ React.useEffect(() => {
324
+ if (!isOpen) return;
325
+ const handleEscape = (event) => {
326
+ if (event.key === "Escape") {
327
+ onClose();
328
+ }
329
+ };
330
+ document.addEventListener("keydown", handleEscape);
331
+ return () => {
332
+ document.removeEventListener("keydown", handleEscape);
333
+ };
334
+ }, [isOpen, onClose]);
335
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: fullWidth ? "block" : "inline-block" }, children: [
336
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: triggerRef, children: trigger }),
337
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx(
338
+ "div",
339
+ {
340
+ ref: popoverRef,
341
+ style: {
342
+ position: "fixed",
343
+ zIndex: 1e3,
344
+ background: colors.bgCard,
345
+ border: "none",
346
+ boxShadow: `0 0 0 1px ${colors.border}, 0 4px 12px rgba(0, 0, 0, 0.15)`,
347
+ minWidth: fullWidth ? triggerWidth : minWidth,
348
+ overflow: "hidden",
349
+ ...width ? { width } : {},
350
+ ...position ? { top: position.top, left: position.left } : { opacity: 0 }
351
+ },
352
+ children
353
+ }
354
+ )
355
+ ] });
356
+ }
357
+ function PopoverHeader({ children, colors }) {
358
+ return /* @__PURE__ */ jsxRuntime.jsx(
359
+ "div",
360
+ {
361
+ style: {
362
+ fontSize: 14,
363
+ fontWeight: 600,
364
+ padding: "12px 12px 0 12px",
365
+ marginBottom: 12,
366
+ color: colors.text
367
+ },
368
+ children
369
+ }
370
+ );
371
+ }
372
+ function PopoverSection({ children }) {
373
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children });
374
+ }
375
+ function PopoverFooter({ children }) {
376
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children });
377
+ }
378
+ var injectedDirections = /* @__PURE__ */ new Set();
379
+ function injectScrollbarStyles(direction) {
380
+ if (typeof document === "undefined") return;
381
+ if (injectedDirections.has(direction)) return;
382
+ injectedDirections.add(direction);
383
+ const scrollClass = scrollbarClassName(direction);
384
+ const isHorizontal = direction === "horizontal" || direction === "both";
385
+ const isVertical = direction === "vertical" || direction === "both";
386
+ const css = `
387
+ .${scrollClass}::-webkit-scrollbar {
388
+ display: block;
389
+ ${isHorizontal ? "height: 8px;" : ""}
390
+ ${isVertical ? "width: 8px;" : ""}
391
+ }
392
+ .${scrollClass}::-webkit-scrollbar-track {
393
+ background: rgba(0, 0, 0, 0.1);
394
+ border-radius: 0;
395
+ }
396
+ .${scrollClass}::-webkit-scrollbar-thumb {
397
+ background: rgba(0, 0, 0, 0.2);
398
+ border-radius: 0;
399
+ }
400
+ .${scrollClass}::-webkit-scrollbar-thumb:hover {
401
+ background: rgba(0, 0, 0, 0.4);
402
+ }
403
+ `;
404
+ const style = document.createElement("style");
405
+ style.setAttribute("data-scroll-container", direction);
406
+ style.textContent = css;
407
+ document.head.appendChild(style);
408
+ }
409
+ function scrollbarClassName(direction) {
410
+ return `scroll-container-${direction}`;
411
+ }
412
+ var ScrollContainer = React.forwardRef(
413
+ ({
414
+ children,
415
+ direction = "both",
416
+ height,
417
+ maxHeight,
418
+ width,
419
+ extendToEdges = false,
420
+ edgePadding = 16,
421
+ className = "",
422
+ style = {},
423
+ onScroll,
424
+ scrollPosition,
425
+ onScrollPositionChange,
426
+ scrollToEnd = false,
427
+ scrollbarPosition = "right"
428
+ }, ref) => {
429
+ const internalRef = React.useRef(null);
430
+ const scrollRef = ref || internalRef;
431
+ const isUpdatingScroll = React.useRef(false);
432
+ const hasScrolledToEnd = React.useRef(false);
433
+ const scrollClass = scrollbarClassName(direction);
434
+ const isHorizontal = direction === "horizontal" || direction === "both";
435
+ const isScrollbarLeft = scrollbarPosition === "left";
436
+ injectScrollbarStyles(direction);
437
+ const handleScroll = React.useCallback((e) => {
438
+ onScroll?.(e);
439
+ if (isUpdatingScroll.current) return;
440
+ if (onScrollPositionChange) {
441
+ const target = e.currentTarget;
442
+ if (isHorizontal) {
443
+ const maxScroll = target.scrollWidth - target.clientWidth;
444
+ if (maxScroll > 0) {
445
+ onScrollPositionChange(target.scrollLeft / maxScroll);
446
+ }
447
+ } else {
448
+ const maxScroll = target.scrollHeight - target.clientHeight;
449
+ if (maxScroll > 0) {
450
+ onScrollPositionChange(target.scrollTop / maxScroll);
451
+ }
452
+ }
453
+ }
454
+ }, [isHorizontal, onScroll, onScrollPositionChange]);
455
+ React.useEffect(() => {
456
+ if (scrollPosition === void 0) return;
457
+ const container = scrollRef.current;
458
+ if (!container) return;
459
+ if (isHorizontal) {
460
+ const maxScroll = container.scrollWidth - container.clientWidth;
461
+ if (maxScroll > 0) {
462
+ const target = scrollPosition * maxScroll;
463
+ if (Math.abs(container.scrollLeft - target) > 1) {
464
+ isUpdatingScroll.current = true;
465
+ container.scrollLeft = target;
466
+ requestAnimationFrame(() => {
467
+ isUpdatingScroll.current = false;
468
+ });
469
+ }
470
+ }
471
+ } else {
472
+ const maxScroll = container.scrollHeight - container.clientHeight;
473
+ if (maxScroll > 0) {
474
+ const target = scrollPosition * maxScroll;
475
+ if (Math.abs(container.scrollTop - target) > 1) {
476
+ isUpdatingScroll.current = true;
477
+ container.scrollTop = target;
478
+ requestAnimationFrame(() => {
479
+ isUpdatingScroll.current = false;
480
+ });
481
+ }
482
+ }
483
+ }
484
+ }, [scrollPosition, isHorizontal, scrollRef]);
485
+ React.useEffect(() => {
486
+ if (!scrollToEnd || hasScrolledToEnd.current) return;
487
+ const container = scrollRef.current;
488
+ if (!container) return;
489
+ const timeoutId = setTimeout(() => {
490
+ if (isHorizontal) {
491
+ container.scrollLeft = container.scrollWidth;
492
+ } else {
493
+ container.scrollTop = container.scrollHeight;
494
+ }
495
+ hasScrolledToEnd.current = true;
496
+ }, 100);
497
+ return () => clearTimeout(timeoutId);
498
+ }, [scrollToEnd, isHorizontal, scrollRef, children]);
499
+ const defaultOverflowX = isHorizontal ? "auto" : "hidden";
500
+ const defaultOverflowY = direction === "vertical" || direction === "both" ? "auto" : "hidden";
501
+ const containerStyle = {
502
+ overflowX: style?.overflowX ?? defaultOverflowX,
503
+ overflowY: style?.overflowY ?? defaultOverflowY,
504
+ ...height !== void 0 ? { height } : {},
505
+ ...maxHeight !== void 0 ? { maxHeight } : {},
506
+ ...width !== void 0 ? { width } : {},
507
+ ...extendToEdges ? { margin: `-${edgePadding}px`, padding: edgePadding } : {},
508
+ ...isScrollbarLeft ? { direction: "rtl" } : {},
509
+ ...style
510
+ };
511
+ return /* @__PURE__ */ jsxRuntime.jsx(
512
+ "div",
513
+ {
514
+ ref: scrollRef,
515
+ className: `${scrollClass} ${className}`.trim(),
516
+ style: containerStyle,
517
+ onScroll: handleScroll,
518
+ children: isScrollbarLeft ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { direction: "ltr", width: "100%", height: "100%" }, children }) : children
519
+ }
520
+ );
521
+ }
522
+ );
523
+ ScrollContainer.displayName = "ScrollContainer";
524
+ var SIZES = {
525
+ sm: { triggerHeight: 32, optionHeight: 32, triggerPadding: "4px 8px", optionPadding: "0 12px", fontSize: 13, chevronSize: 14 },
526
+ md: { triggerHeight: 40, optionHeight: 40, triggerPadding: "8px 12px", optionPadding: "0 12px", fontSize: 14, chevronSize: 16 }
527
+ };
528
+ function MenuSelect({
529
+ options,
530
+ value,
531
+ onChange,
532
+ placeholder = "Select...",
533
+ isDisabled = false,
534
+ colors,
535
+ size = "md",
536
+ minWidth,
537
+ maxDropdownHeight = 240,
538
+ placement = "bottom-start",
539
+ renderTrigger
540
+ }) {
541
+ const [isOpen, setIsOpen] = React.useState(false);
542
+ const [focusedIndex, setFocusedIndex] = React.useState(-1);
543
+ const listRef = React.useRef(null);
544
+ const selectedOption = options.find((o) => o.value === value);
545
+ const s = SIZES[size];
546
+ const handleSelect = React.useCallback(
547
+ (optionValue) => {
548
+ onChange(optionValue);
549
+ setIsOpen(false);
550
+ },
551
+ [onChange]
552
+ );
553
+ const handleToggle = React.useCallback(() => {
554
+ if (isDisabled) return;
555
+ setIsOpen((prev) => {
556
+ if (!prev) {
557
+ const idx = options.findIndex((o) => o.value === value && !o.isDisabled);
558
+ setFocusedIndex(idx >= 0 ? idx : options.findIndex((o) => !o.isDisabled));
559
+ }
560
+ return !prev;
561
+ });
562
+ }, [isDisabled, options, value]);
563
+ const findNextEnabled = React.useCallback((from, direction) => {
564
+ let idx = from + direction;
565
+ while (idx >= 0 && idx < options.length) {
566
+ if (!options[idx]?.isDisabled) return idx;
567
+ idx += direction;
568
+ }
569
+ return from;
570
+ }, [options]);
571
+ const handleKeyDown = React.useCallback(
572
+ (e) => {
573
+ if (!isOpen) {
574
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
575
+ e.preventDefault();
576
+ handleToggle();
577
+ }
578
+ return;
579
+ }
580
+ switch (e.key) {
581
+ case "ArrowDown":
582
+ e.preventDefault();
583
+ setFocusedIndex((prev) => findNextEnabled(prev, 1));
584
+ break;
585
+ case "ArrowUp":
586
+ e.preventDefault();
587
+ setFocusedIndex((prev) => findNextEnabled(prev, -1));
588
+ break;
589
+ case "Enter":
590
+ case " ":
591
+ e.preventDefault();
592
+ if (focusedIndex >= 0 && focusedIndex < options.length && !options[focusedIndex]?.isDisabled) {
593
+ handleSelect(options[focusedIndex].value);
594
+ }
595
+ break;
596
+ case "Escape":
597
+ e.preventDefault();
598
+ setIsOpen(false);
599
+ break;
600
+ }
601
+ },
602
+ [isOpen, focusedIndex, options, handleSelect, handleToggle, findNextEnabled]
603
+ );
604
+ React.useEffect(() => {
605
+ if (!isOpen || focusedIndex < 0 || !listRef.current) return;
606
+ const listbox = listRef.current.querySelector("[role=listbox]");
607
+ if (!listbox) return;
608
+ const items = listbox.children;
609
+ if (items[focusedIndex]) {
610
+ items[focusedIndex].scrollIntoView({ block: "nearest" });
611
+ }
612
+ }, [isOpen, focusedIndex]);
613
+ const triggerElement = renderTrigger ? /* @__PURE__ */ jsxRuntime.jsx(
614
+ "div",
615
+ {
616
+ role: "button",
617
+ tabIndex: isDisabled ? -1 : 0,
618
+ onClick: handleToggle,
619
+ onKeyDown: handleKeyDown,
620
+ style: { cursor: isDisabled ? "not-allowed" : "pointer" },
621
+ children: renderTrigger({ selected: selectedOption, isOpen, isDisabled })
622
+ }
623
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
624
+ "button",
625
+ {
626
+ type: "button",
627
+ onClick: handleToggle,
628
+ onKeyDown: handleKeyDown,
629
+ disabled: isDisabled,
630
+ style: {
631
+ display: "flex",
632
+ alignItems: "center",
633
+ justifyContent: "space-between",
634
+ gap: 8,
635
+ width: "100%",
636
+ minWidth,
637
+ padding: s.triggerPadding,
638
+ fontSize: s.fontSize,
639
+ fontWeight: 400,
640
+ fontFamily: "inherit",
641
+ border: "none",
642
+ borderRadius: 0,
643
+ boxShadow: `0 0 0 1px ${colors.border}`,
644
+ background: colors.bg,
645
+ color: selectedOption ? colors.text : colors.textMuted,
646
+ cursor: isDisabled ? "not-allowed" : "pointer",
647
+ opacity: isDisabled ? 0.5 : 1,
648
+ minHeight: s.triggerHeight
649
+ },
650
+ children: [
651
+ /* @__PURE__ */ jsxRuntime.jsxs(
652
+ "span",
653
+ {
654
+ style: {
655
+ display: "flex",
656
+ alignItems: "center",
657
+ gap: 8,
658
+ overflow: "hidden"
659
+ },
660
+ children: [
661
+ selectedOption?.icon,
662
+ /* @__PURE__ */ jsxRuntime.jsx(
663
+ "span",
664
+ {
665
+ style: {
666
+ overflow: "hidden",
667
+ textOverflow: "ellipsis",
668
+ whiteSpace: "nowrap"
669
+ },
670
+ children: selectedOption?.label ?? placeholder
671
+ }
672
+ )
673
+ ]
674
+ }
675
+ ),
676
+ /* @__PURE__ */ jsxRuntime.jsx(
677
+ react$1.Icon,
678
+ {
679
+ icon: "feather:chevron-down",
680
+ width: s.chevronSize,
681
+ height: s.chevronSize,
682
+ style: {
683
+ flexShrink: 0,
684
+ transition: "transform 0.2s",
685
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)"
686
+ }
687
+ }
688
+ )
689
+ ]
690
+ }
691
+ );
692
+ return /* @__PURE__ */ jsxRuntime.jsx(
693
+ Popover,
694
+ {
695
+ isOpen,
696
+ onClose: () => setIsOpen(false),
697
+ trigger: triggerElement,
698
+ colors,
699
+ minWidth,
700
+ fullWidth: !renderTrigger,
701
+ gap: 0,
702
+ placement,
703
+ children: /* @__PURE__ */ jsxRuntime.jsx(
704
+ ScrollContainer,
705
+ {
706
+ ref: listRef,
707
+ direction: "vertical",
708
+ maxHeight: maxDropdownHeight,
709
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", onKeyDown: handleKeyDown, children: options.map((option, index) => {
710
+ const isSelected = option.value === value;
711
+ const isFocused = index === focusedIndex;
712
+ const optDisabled = option.isDisabled ?? false;
713
+ return /* @__PURE__ */ jsxRuntime.jsxs(
714
+ "button",
715
+ {
716
+ type: "button",
717
+ role: "option",
718
+ "aria-selected": isSelected,
719
+ "aria-disabled": optDisabled,
720
+ onClick: () => !optDisabled && handleSelect(option.value),
721
+ onMouseEnter: () => !optDisabled && setFocusedIndex(index),
722
+ style: {
723
+ display: "flex",
724
+ alignItems: "center",
725
+ gap: 8,
726
+ width: "100%",
727
+ padding: s.optionPadding,
728
+ minHeight: s.optionHeight,
729
+ fontSize: s.fontSize,
730
+ fontFamily: "inherit",
731
+ border: "none",
732
+ borderRadius: 0,
733
+ background: isFocused && !optDisabled ? `${colors.border}40` : "transparent",
734
+ color: optDisabled ? colors.textMuted : colors.text,
735
+ cursor: optDisabled ? "not-allowed" : "pointer",
736
+ fontWeight: isSelected ? 500 : 400,
737
+ textAlign: "left",
738
+ opacity: optDisabled ? 0.5 : 1
739
+ },
740
+ children: [
741
+ option.icon,
742
+ /* @__PURE__ */ jsxRuntime.jsx(
743
+ "span",
744
+ {
745
+ style: {
746
+ overflow: "hidden",
747
+ textOverflow: "ellipsis",
748
+ whiteSpace: "nowrap",
749
+ flex: 1
750
+ },
751
+ children: option.label
752
+ }
753
+ ),
754
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 14, flexShrink: 0, display: "flex", justifyContent: "center" }, children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
755
+ react$1.Icon,
756
+ {
757
+ icon: "feather:check",
758
+ width: 14,
759
+ height: 14
760
+ }
761
+ ) })
762
+ ]
763
+ },
764
+ option.value
765
+ );
766
+ }) })
767
+ }
768
+ )
769
+ }
770
+ );
771
+ }
772
+ function MultiSelectFilter({
773
+ allItems,
774
+ selectedItems,
775
+ onSelectionChange,
776
+ itemStats,
777
+ renderIcon,
778
+ renderListIcon,
779
+ getLabel,
780
+ title,
781
+ colors,
782
+ placeholder: _placeholder,
783
+ buttonWidth = 112,
784
+ iconSize = 24,
785
+ iconLimit = 3,
786
+ iconOverlap = 8,
787
+ reverseZIndex = true,
788
+ getVolumeRank
789
+ }) {
790
+ const [isOpen, setIsOpen] = React.useState(false);
791
+ const [localSelection, setLocalSelection] = React.useState(selectedItems);
792
+ const statsMap = React.useMemo(
793
+ () => new Map(itemStats.map((stat) => [stat.item, stat.count])),
794
+ [itemStats]
795
+ );
796
+ const sortedItems = React.useMemo(() => {
797
+ return Array.from(allItems).sort((a, b) => {
798
+ if (getVolumeRank) {
799
+ const rankA = getVolumeRank(a);
800
+ const rankB = getVolumeRank(b);
801
+ if (rankA !== rankB) {
802
+ return rankA - rankB;
803
+ }
804
+ }
805
+ const countA = statsMap.get(a) ?? 0;
806
+ const countB = statsMap.get(b) ?? 0;
807
+ if (countA !== countB) {
808
+ return countB - countA;
809
+ }
810
+ return getLabel(a).localeCompare(getLabel(b));
811
+ });
812
+ }, [allItems, getVolumeRank, statsMap, getLabel]);
813
+ const handleToggle = (item) => {
814
+ const newSelection = new Set(localSelection);
815
+ if (newSelection.has(item)) {
816
+ newSelection.delete(item);
817
+ } else {
818
+ newSelection.add(item);
819
+ }
820
+ setLocalSelection(newSelection);
821
+ };
822
+ const handleSelectAll = () => {
823
+ setLocalSelection(/* @__PURE__ */ new Set());
824
+ };
825
+ const handleSelectNone = () => {
826
+ setLocalSelection(/* @__PURE__ */ new Set());
827
+ };
828
+ const handleApply = () => {
829
+ onSelectionChange(localSelection);
830
+ setIsOpen(false);
831
+ };
832
+ const handleToggleOpen = () => {
833
+ if (isOpen) {
834
+ setIsOpen(false);
835
+ } else {
836
+ setLocalSelection(selectedItems);
837
+ setIsOpen(true);
838
+ }
839
+ };
840
+ const selectedCount = selectedItems.size;
841
+ const totalCount = allItems.size;
842
+ const isShowingAll = selectedCount === 0 || selectedCount === totalCount;
843
+ const hasFilter = selectedCount > 0 && selectedCount < totalCount;
844
+ const itemsToDisplay = React.useMemo(() => {
845
+ if (isShowingAll) {
846
+ return sortedItems;
847
+ }
848
+ return Array.from(selectedItems).sort((a, b) => {
849
+ if (getVolumeRank) {
850
+ const rankA = getVolumeRank(a);
851
+ const rankB = getVolumeRank(b);
852
+ if (rankA !== rankB) {
853
+ return rankA - rankB;
854
+ }
855
+ }
856
+ const countA = statsMap.get(a) ?? 0;
857
+ const countB = statsMap.get(b) ?? 0;
858
+ if (countA !== countB) {
859
+ return countB - countA;
860
+ }
861
+ return getLabel(a).localeCompare(getLabel(b));
862
+ });
863
+ }, [isShowingAll, sortedItems, selectedItems, getVolumeRank, statsMap, getLabel]);
864
+ const iconItems = itemsToDisplay.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
865
+ "div",
866
+ {
867
+ style: {
868
+ width: iconSize,
869
+ height: iconSize,
870
+ borderRadius: "50%",
871
+ boxShadow: `0 0 0 1px ${colors.border}`,
872
+ overflow: "hidden",
873
+ background: colors.bgCard,
874
+ display: "flex",
875
+ alignItems: "center",
876
+ justifyContent: "center"
877
+ },
878
+ children: renderIcon(item)
879
+ },
880
+ item
881
+ ));
882
+ const triggerButton = /* @__PURE__ */ jsxRuntime.jsx(
883
+ "button",
884
+ {
885
+ onClick: handleToggleOpen,
886
+ style: {
887
+ padding: "8px 12px",
888
+ fontSize: 13,
889
+ fontWeight: 500,
890
+ border: `1px solid ${hasFilter ? "#3182ce" : colors.border}`,
891
+ background: hasFilter ? "#3182ce" : colors.bgCard,
892
+ color: hasFilter ? "#fff" : colors.text,
893
+ cursor: "pointer",
894
+ display: "flex",
895
+ alignItems: "center",
896
+ gap: 6,
897
+ minWidth: buttonWidth,
898
+ maxWidth: buttonWidth,
899
+ minHeight: 38,
900
+ maxHeight: 38,
901
+ justifyContent: "center"
902
+ },
903
+ children: /* @__PURE__ */ jsxRuntime.jsx(
904
+ OverlappingStack,
905
+ {
906
+ items: iconItems,
907
+ limit: iconLimit,
908
+ overlap: iconOverlap,
909
+ itemSize: iconSize,
910
+ reverseZIndex
911
+ }
912
+ )
913
+ }
914
+ );
915
+ const listIconRenderer = renderListIcon ?? renderIcon;
916
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { isOpen, onClose: () => setIsOpen(false), trigger: triggerButton, colors, children: [
917
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverHeader, { colors, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
918
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
919
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 4 }, children: [
920
+ /* @__PURE__ */ jsxRuntime.jsx(
921
+ "button",
922
+ {
923
+ onClick: handleSelectAll,
924
+ style: {
925
+ padding: "2px 8px",
926
+ fontSize: 11,
927
+ border: "none",
928
+ background: "transparent",
929
+ color: colors.textMuted,
930
+ cursor: "pointer"
931
+ },
932
+ children: "All"
933
+ }
934
+ ),
935
+ /* @__PURE__ */ jsxRuntime.jsx(
936
+ "button",
937
+ {
938
+ onClick: handleSelectNone,
939
+ style: {
940
+ padding: "2px 8px",
941
+ fontSize: 11,
942
+ border: "none",
943
+ background: "transparent",
944
+ color: colors.textMuted,
945
+ cursor: "pointer"
946
+ },
947
+ children: "None"
948
+ }
949
+ )
950
+ ] })
951
+ ] }) }),
952
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverSection, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
953
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { maxHeight: 300, overflowY: "auto", paddingBottom: 12 }, children: sortedItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
954
+ "label",
955
+ {
956
+ style: {
957
+ display: "flex",
958
+ alignItems: "center",
959
+ justifyContent: "space-between",
960
+ padding: "4px 12px",
961
+ cursor: "pointer",
962
+ fontSize: 13
963
+ },
964
+ children: [
965
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
966
+ /* @__PURE__ */ jsxRuntime.jsx(
967
+ "input",
968
+ {
969
+ type: "checkbox",
970
+ checked: localSelection.has(item),
971
+ onChange: () => handleToggle(item),
972
+ style: { cursor: "pointer" }
973
+ }
974
+ ),
975
+ /* @__PURE__ */ jsxRuntime.jsx(
976
+ "div",
977
+ {
978
+ style: {
979
+ width: iconSize,
980
+ height: iconSize,
981
+ borderRadius: "50%",
982
+ boxShadow: `0 0 0 1px ${colors.border}`,
983
+ overflow: "hidden",
984
+ background: colors.bgCard,
985
+ display: "flex",
986
+ alignItems: "center",
987
+ justifyContent: "center",
988
+ flexShrink: 0
989
+ },
990
+ children: listIconRenderer(item)
991
+ }
992
+ ),
993
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: getLabel(item) })
994
+ ] }),
995
+ statsMap.size > 0 && /* @__PURE__ */ jsxRuntime.jsx(
996
+ "span",
997
+ {
998
+ style: {
999
+ fontSize: 11,
1000
+ color: colors.textMuted,
1001
+ background: colors.bg,
1002
+ padding: "2px 6px"
1003
+ },
1004
+ children: statsMap.get(item) ?? 0
1005
+ }
1006
+ )
1007
+ ]
1008
+ },
1009
+ item
1010
+ )) }),
1011
+ /* @__PURE__ */ jsxRuntime.jsx(
1012
+ "div",
1013
+ {
1014
+ style: {
1015
+ position: "absolute",
1016
+ bottom: 0,
1017
+ left: 0,
1018
+ right: 0,
1019
+ height: 12,
1020
+ background: `linear-gradient(to bottom, transparent, ${colors.bgCard})`,
1021
+ pointerEvents: "none"
1022
+ }
1023
+ }
1024
+ )
1025
+ ] }) }),
1026
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(
1027
+ "button",
1028
+ {
1029
+ onClick: handleApply,
1030
+ style: {
1031
+ width: "100%",
1032
+ padding: "8px 12px",
1033
+ fontSize: 13,
1034
+ fontWeight: 500,
1035
+ border: "none",
1036
+ background: "#3182ce",
1037
+ color: "#fff",
1038
+ cursor: "pointer",
1039
+ minHeight: 38
1040
+ },
1041
+ children: "Apply"
1042
+ }
1043
+ ) })
1044
+ ] });
1045
+ }
1046
+ var PaymentCurrencyCombo = ({
1047
+ paymentMethodIcon,
1048
+ currencyId,
1049
+ size = 20,
1050
+ colors
1051
+ }) => {
1052
+ const badgeSize = size * 0.6;
1053
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1054
+ "div",
1055
+ {
1056
+ style: {
1057
+ display: "flex",
1058
+ alignItems: "center",
1059
+ justifyContent: "center",
1060
+ boxSizing: "border-box",
1061
+ borderRadius: "100%",
1062
+ height: size,
1063
+ width: size,
1064
+ position: "relative",
1065
+ overflow: "hidden"
1066
+ },
1067
+ children: [
1068
+ /* @__PURE__ */ jsxRuntime.jsx(
1069
+ "div",
1070
+ {
1071
+ style: {
1072
+ position: "absolute",
1073
+ top: 0,
1074
+ left: 0,
1075
+ width: "100%",
1076
+ height: "100%",
1077
+ display: "flex",
1078
+ alignItems: "center",
1079
+ justifyContent: "center",
1080
+ overflow: "hidden"
1081
+ },
1082
+ children: /* @__PURE__ */ jsxRuntime.jsx(CurrencyIcon, { currencyId, size })
1083
+ }
1084
+ ),
1085
+ /* @__PURE__ */ jsxRuntime.jsx(
1086
+ "div",
1087
+ {
1088
+ style: {
1089
+ position: "absolute",
1090
+ bottom: 0,
1091
+ right: 0,
1092
+ width: badgeSize,
1093
+ height: badgeSize,
1094
+ display: "flex",
1095
+ alignItems: "center",
1096
+ justifyContent: "center",
1097
+ background: colors.bg,
1098
+ borderRadius: 0
1099
+ },
1100
+ children: paymentMethodIcon
1101
+ }
1102
+ )
1103
+ ]
1104
+ }
1105
+ );
1106
+ };
1107
+ function PaymentMethodIcon({
1108
+ icon,
1109
+ color = "#6b7280",
1110
+ backgroundColor,
1111
+ size = 16,
1112
+ isUnknown = false,
1113
+ tooltip
1114
+ }) {
1115
+ const effectiveColor = isUnknown ? "#ef4444" : color;
1116
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1117
+ "span",
1118
+ {
1119
+ style: {
1120
+ display: "flex",
1121
+ alignItems: "center",
1122
+ justifyContent: "center",
1123
+ width: size,
1124
+ height: size,
1125
+ opacity: isUnknown ? 0.6 : 1,
1126
+ position: "relative",
1127
+ backgroundColor,
1128
+ borderRadius: backgroundColor ? "4px" : void 0,
1129
+ padding: backgroundColor ? "2px" : void 0,
1130
+ boxSizing: "border-box"
1131
+ },
1132
+ title: tooltip,
1133
+ children: [
1134
+ /* @__PURE__ */ jsxRuntime.jsx(
1135
+ react$1.Icon,
1136
+ {
1137
+ icon,
1138
+ width: backgroundColor ? size - 4 : size,
1139
+ height: backgroundColor ? size - 4 : size,
1140
+ style: { color: effectiveColor }
1141
+ }
1142
+ ),
1143
+ isUnknown && /* @__PURE__ */ jsxRuntime.jsx(
1144
+ "span",
1145
+ {
1146
+ style: {
1147
+ position: "absolute",
1148
+ bottom: -2,
1149
+ right: -2,
1150
+ width: size * 0.35,
1151
+ height: size * 0.35,
1152
+ borderRadius: "50%",
1153
+ background: "#ef4444",
1154
+ border: "1px solid white"
1155
+ },
1156
+ title: "Unknown payment method"
1157
+ }
1158
+ )
1159
+ ]
1160
+ }
1161
+ );
1162
+ }
1163
+ var DEFAULT_PAYMENT_ICON = "mdi:credit-card";
1164
+ var DEFAULT_PAYMENT_COLOR = "#6b7280";
1165
+ function Tooltip({ label, children, isDisabled, placement = "top", display = "inline-flex" }) {
1166
+ const [visible, setVisible] = React.useState(false);
1167
+ const ref = React.useRef(null);
1168
+ React.useEffect(() => {
1169
+ if (isDisabled) setVisible(false);
1170
+ }, [isDisabled]);
1171
+ if (isDisabled) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1172
+ const tipStyle = {
1173
+ position: "absolute",
1174
+ left: 0,
1175
+ width: "max-content",
1176
+ maxWidth: 280,
1177
+ background: "rgba(0,0,0,0.82)",
1178
+ color: "#fff",
1179
+ fontSize: 11,
1180
+ lineHeight: 1.4,
1181
+ padding: "4px 8px",
1182
+ whiteSpace: "normal",
1183
+ zIndex: 9999,
1184
+ pointerEvents: "none",
1185
+ borderRadius: 2,
1186
+ ...placement === "top" ? { bottom: "calc(100% + 5px)" } : { top: "calc(100% + 5px)" }
1187
+ };
1188
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1189
+ "span",
1190
+ {
1191
+ ref,
1192
+ style: { position: "relative", display, alignItems: display === "inline-flex" ? "center" : void 0 },
1193
+ onMouseEnter: () => setVisible(true),
1194
+ onMouseLeave: () => setVisible(false),
1195
+ children: [
1196
+ children,
1197
+ visible && /* @__PURE__ */ jsxRuntime.jsx("span", { style: tipStyle, children: label })
1198
+ ]
1199
+ }
1200
+ );
1201
+ }
1202
+ function VersionBadge(props) {
1203
+ const { version, colors, style } = props;
1204
+ const bgColor = version === "v3" ? colors.bgV3 : colors.bgV2;
1205
+ return /* @__PURE__ */ jsxRuntime.jsx(
1206
+ "span",
1207
+ {
1208
+ style: {
1209
+ display: "inline-flex",
1210
+ alignItems: "center",
1211
+ justifyContent: "center",
1212
+ padding: "2px 4px",
1213
+ fontSize: "10px",
1214
+ fontWeight: 600,
1215
+ textTransform: "uppercase",
1216
+ letterSpacing: "0.25px",
1217
+ borderRadius: "4px",
1218
+ backgroundColor: bgColor,
1219
+ color: colors.text,
1220
+ ...style
1221
+ },
1222
+ children: version
1223
+ }
1224
+ );
1225
+ }
1226
+
1227
+ exports.Card = Card;
1228
+ exports.CardHorizontalRule = CardHorizontalRule;
1229
+ exports.CurrencyIcon = CurrencyIcon;
1230
+ exports.DEFAULT_PAYMENT_COLOR = DEFAULT_PAYMENT_COLOR;
1231
+ exports.DEFAULT_PAYMENT_ICON = DEFAULT_PAYMENT_ICON;
1232
+ exports.DepositIdentifier = DepositIdentifier;
1233
+ exports.FlagIcon = FlagIcon;
1234
+ exports.MenuSelect = MenuSelect;
1235
+ exports.MultiSelectFilter = MultiSelectFilter;
1236
+ exports.OverlappingStack = OverlappingStack;
1237
+ exports.PaymentCurrencyCombo = PaymentCurrencyCombo;
1238
+ exports.PaymentMethodIcon = PaymentMethodIcon;
1239
+ exports.Popover = Popover;
1240
+ exports.PopoverFooter = PopoverFooter;
1241
+ exports.PopoverHeader = PopoverHeader;
1242
+ exports.PopoverSection = PopoverSection;
1243
+ exports.ScrollContainer = ScrollContainer;
1244
+ exports.Tooltip = Tooltip;
1245
+ exports.VersionBadge = VersionBadge;
1246
+ exports.injectScrollbarStyles = injectScrollbarStyles;
1247
+ exports.scrollbarClassName = scrollbarClassName;
1248
+ //# sourceMappingURL=index.cjs.map
1249
+ //# sourceMappingURL=index.cjs.map