@provex/components 1.2.3

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