@trustwallet/connect-react 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/LICENSE.md +76 -0
  2. package/README.md +567 -0
  3. package/cli.js +144 -0
  4. package/dist/global-NUJAOJRO.css +132 -0
  5. package/dist/index.d.ts +13 -0
  6. package/dist/index.js +705 -0
  7. package/dist/styles.module-27BC6Q35.module.css +7 -0
  8. package/dist/styles.module-2IXPYHX5.module.css +33 -0
  9. package/dist/styles.module-4I5UVTXZ.module.css +34 -0
  10. package/dist/styles.module-4QHLSLTJ.module.css +9 -0
  11. package/dist/styles.module-5C4YIUDE.module.css +26 -0
  12. package/dist/styles.module-5FM5SDSC.module.css +8 -0
  13. package/dist/styles.module-5L4UXGVZ.module.css +62 -0
  14. package/dist/styles.module-5S7A3MI3.module.css +6 -0
  15. package/dist/styles.module-5TDOAM3X.module.css +5 -0
  16. package/dist/styles.module-BH6BB6HX.module.css +23 -0
  17. package/dist/styles.module-FMH4FUT5.module.css +35 -0
  18. package/dist/styles.module-GCRMKZIZ.module.css +9 -0
  19. package/dist/styles.module-GSNHG7ET.module.css +92 -0
  20. package/dist/styles.module-HR3VQZUH.module.css +44 -0
  21. package/dist/styles.module-IZSJLNB4.module.css +9 -0
  22. package/dist/styles.module-JFMC6A5G.module.css +6 -0
  23. package/dist/styles.module-KIOD6JAJ.module.css +5 -0
  24. package/dist/styles.module-NDDQJHO2.module.css +21 -0
  25. package/dist/styles.module-PXUDS327.module.css +7 -0
  26. package/dist/styles.module-RKS6GYHN.module.css +6 -0
  27. package/dist/styles.module-U5TRY5PT.module.css +123 -0
  28. package/dist/styles.module-U6N4VKTA.module.css +72 -0
  29. package/dist/styles.module-VWMJAIVE.module.css +20 -0
  30. package/dist/styles.module-WGWNXHEK.module.css +5 -0
  31. package/dist/styles.module-Y3MUWEOL.module.css +5 -0
  32. package/dist/styles.module-YJSZIZTZ.module.css +32 -0
  33. package/dist/styles.module-ZISFY5AO.module.css +15 -0
  34. package/dist/styles.module-ZPYBUUD6.module.css +8 -0
  35. package/package.json +53 -0
  36. package/src/context/index.tsx +25 -0
  37. package/src/index.ts +9 -0
  38. package/src/styles/global.css +132 -0
  39. package/src/types/css-modules.d.ts +4 -0
  40. package/src/ui/TrustModal.tsx +31 -0
  41. package/src/ui/buttons/GetTrustButton/components/GetTrustMessage/index.tsx +10 -0
  42. package/src/ui/buttons/GetTrustButton/components/GetTrustMessage/styles.module.css +7 -0
  43. package/src/ui/buttons/GetTrustButton/components/GetTrustWrapper/index.tsx +10 -0
  44. package/src/ui/buttons/GetTrustButton/components/GetTrustWrapper/styles.module.css +5 -0
  45. package/src/ui/buttons/GetTrustButton/index.tsx +21 -0
  46. package/src/ui/buttons/NamespaceButton/index.tsx +62 -0
  47. package/src/ui/buttons/NamespaceButton/styles.module.css +72 -0
  48. package/src/ui/buttons/WalletButton/index.tsx +76 -0
  49. package/src/ui/buttons/WalletButton/styles.module.css +123 -0
  50. package/src/ui/buttons/WalletConnectButton/components/WCGrid/index.tsx +10 -0
  51. package/src/ui/buttons/WalletConnectButton/components/WCGrid/styles.module.css +5 -0
  52. package/src/ui/buttons/WalletConnectButton/components/WCTitle/index.tsx +5 -0
  53. package/src/ui/buttons/WalletConnectButton/components/WCTitle/styles.module.css +8 -0
  54. package/src/ui/buttons/WalletConnectButton/index.tsx +16 -0
  55. package/src/ui/components/Footer/components/FooterDescription/index.tsx +10 -0
  56. package/src/ui/components/Footer/components/FooterDescription/styles.module.css +6 -0
  57. package/src/ui/components/Footer/components/FooterLink/index.tsx +15 -0
  58. package/src/ui/components/Footer/components/FooterLink/styles.module.css +20 -0
  59. package/src/ui/components/Footer/components/FooterWrapper/index.tsx +10 -0
  60. package/src/ui/components/Footer/components/FooterWrapper/styles.module.css +21 -0
  61. package/src/ui/components/Footer/index.tsx +16 -0
  62. package/src/ui/icons/BackIcon.tsx +14 -0
  63. package/src/ui/icons/CheckIcon.tsx +17 -0
  64. package/src/ui/icons/ClearIcon.tsx +13 -0
  65. package/src/ui/icons/CloseIcon.tsx +14 -0
  66. package/src/ui/icons/CopyIcon.tsx +16 -0
  67. package/src/ui/icons/SearchIcon.tsx +20 -0
  68. package/src/ui/icons/Spinner.tsx +7 -0
  69. package/src/ui/inputs/SearchBar/index.tsx +44 -0
  70. package/src/ui/inputs/SearchBar/styles.module.css +62 -0
  71. package/src/ui/layout/ModalBody/index.tsx +15 -0
  72. package/src/ui/layout/ModalBody/styles.module.css +15 -0
  73. package/src/ui/layout/ModalError/index.tsx +9 -0
  74. package/src/ui/layout/ModalError/styles.module.css +9 -0
  75. package/src/ui/layout/ModalHeader/index.tsx +26 -0
  76. package/src/ui/layout/ModalHeader/styles.module.css +92 -0
  77. package/src/ui/layout/ModalOverlay/index.tsx +15 -0
  78. package/src/ui/layout/ModalOverlay/styles.module.css +33 -0
  79. package/src/ui/layout/ModalWrapper/index.tsx +23 -0
  80. package/src/ui/layout/ModalWrapper/styles.module.css +26 -0
  81. package/src/ui/views/MobileWalletsView/components/MobileEmptyState/index.tsx +13 -0
  82. package/src/ui/views/MobileWalletsView/components/MobileEmptyState/styles.module.css +9 -0
  83. package/src/ui/views/MobileWalletsView/components/MobileLoading/index.tsx +5 -0
  84. package/src/ui/views/MobileWalletsView/components/MobileLoading/styles.module.css +9 -0
  85. package/src/ui/views/MobileWalletsView/components/MobileSearch/index.tsx +36 -0
  86. package/src/ui/views/MobileWalletsView/components/MobileSearch/styles.module.css +44 -0
  87. package/src/ui/views/MobileWalletsView/components/MobileWalletsList/index.tsx +46 -0
  88. package/src/ui/views/MobileWalletsView/components/MobileWalletsList/styles.module.css +35 -0
  89. package/src/ui/views/MobileWalletsView/components/MobileWrapper/index.tsx +10 -0
  90. package/src/ui/views/MobileWalletsView/components/MobileWrapper/styles.module.css +6 -0
  91. package/src/ui/views/MobileWalletsView/index.tsx +22 -0
  92. package/src/ui/views/NamespaceView/components/NamespaceGrid/index.tsx +10 -0
  93. package/src/ui/views/NamespaceView/components/NamespaceGrid/styles.module.css +5 -0
  94. package/src/ui/views/NamespaceView/components/NamespaceHeader/index.tsx +20 -0
  95. package/src/ui/views/NamespaceView/components/NamespaceHeader/styles.module.css +34 -0
  96. package/src/ui/views/NamespaceView/index.tsx +16 -0
  97. package/src/ui/views/QRView/components/QRActions/index.tsx +11 -0
  98. package/src/ui/views/QRView/components/QRActions/styles.module.css +8 -0
  99. package/src/ui/views/QRView/components/QRButton/index.tsx +15 -0
  100. package/src/ui/views/QRView/components/QRButton/styles.module.css +32 -0
  101. package/src/ui/views/QRView/components/QRError/index.tsx +9 -0
  102. package/src/ui/views/QRView/components/QRError/styles.module.css +5 -0
  103. package/src/ui/views/QRView/components/QRPlaceholder/index.tsx +10 -0
  104. package/src/ui/views/QRView/components/QRPlaceholder/styles.module.css +7 -0
  105. package/src/ui/views/QRView/components/QRWrapper/index.tsx +14 -0
  106. package/src/ui/views/QRView/components/QRWrapper/styles.module.css +23 -0
  107. package/src/ui/views/QRView/index.tsx +24 -0
  108. package/src/ui/views/WalletsView/components/WalletsGrid/index.tsx +10 -0
  109. package/src/ui/views/WalletsView/components/WalletsGrid/styles.module.css +5 -0
  110. package/src/ui/views/WalletsView/components/WalletsHeader/index.tsx +9 -0
  111. package/src/ui/views/WalletsView/components/WalletsHeader/styles.module.css +6 -0
  112. package/src/ui/views/WalletsView/index.tsx +20 -0
@@ -0,0 +1,123 @@
1
+ .walletButton {
2
+ background: var(--tcui-card);
3
+ border: 1px solid var(--tcui-border);
4
+ border-radius: var(--tcui-radius);
5
+ padding: 12px 14px;
6
+ min-height: 64px;
7
+ display: flex;
8
+ align-items: center;
9
+ gap: 12px;
10
+ cursor: pointer;
11
+ transition: all 0.15s ease;
12
+ position: relative;
13
+ }
14
+
15
+ .walletButton:hover {
16
+ background: var(--tcui-card-hover);
17
+ }
18
+
19
+ .walletButton:active {
20
+ transform: scale(0.99);
21
+ }
22
+
23
+ .walletButton[data-active='true'] {
24
+ border: 1px solid var(--tcui-accent);
25
+ background: var(--tcui-card-hover);
26
+ }
27
+
28
+ [data-tcui-theme='light'] .walletButton[data-active='true'] {
29
+ border-color: var(--tcui-connect-text);
30
+ }
31
+
32
+ .walletButton[data-variant='walletconnect'] {
33
+ border-color: var(--tcui-border);
34
+ background: var(--tcui-card);
35
+ }
36
+
37
+ .walletButton[data-variant='walletconnect']:hover {
38
+ background: var(--tcui-card-hover);
39
+ }
40
+
41
+ .walletButton[data-disabled='true'] {
42
+ opacity: 0.5;
43
+ cursor: not-allowed;
44
+ }
45
+
46
+ .walletButton[data-disabled='true']:hover {
47
+ background: var(--tcui-card);
48
+ transform: none;
49
+ }
50
+
51
+ .walletButton[data-disabled='true']:active {
52
+ transform: none;
53
+ }
54
+
55
+ .walletButton[data-disabled='true'] :global(.tcui-button) {
56
+ cursor: not-allowed;
57
+ }
58
+
59
+ .icon {
60
+ width: 40px;
61
+ height: 40px;
62
+ border-radius: 6px;
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ overflow: hidden;
67
+ flex-shrink: 0;
68
+ }
69
+
70
+ .icon img {
71
+ width: 100%;
72
+ height: 100%;
73
+ }
74
+
75
+ .meta {
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: 2px;
79
+ flex: 1;
80
+ min-width: 0;
81
+ }
82
+
83
+ .name {
84
+ font-weight: 600;
85
+ font-size: 15px;
86
+ line-height: 1.4;
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 6px;
90
+ }
91
+
92
+ .namespaces {
93
+ display: flex;
94
+ align-items: center;
95
+ margin-top: 0.2px;
96
+ }
97
+
98
+ .namespaces img {
99
+ width: 15px;
100
+ height: 15px;
101
+ border-radius: 4px;
102
+ flex-shrink: 0;
103
+ margin-left: -3px;
104
+ }
105
+
106
+ .namespaces img:first-child {
107
+ margin-left: 0;
108
+ }
109
+
110
+ .error {
111
+ font-size: 12px;
112
+ color: var(--tcui-danger);
113
+ line-height: 1.4;
114
+ margin-top: 2px;
115
+ }
116
+
117
+ .walletButton:hover :global(.tcui-button-primary) {
118
+ background: var(--tcui-connect-hover);
119
+ }
120
+
121
+ .walletButton:hover :global(.tcui-button:not(.tcui-button-primary)) {
122
+ background: var(--tcui-disconnect-hover);
123
+ }
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from 'react'
2
+ import styles from './styles.module.css'
3
+
4
+ interface WCGridProps {
5
+ children: ReactNode
6
+ }
7
+
8
+ export function WCGrid({ children }: WCGridProps) {
9
+ return <div className={styles.grid}>{children}</div>
10
+ }
@@ -0,0 +1,5 @@
1
+ .grid {
2
+ display: grid;
3
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
4
+ gap: 8px;
5
+ }
@@ -0,0 +1,5 @@
1
+ import styles from './styles.module.css'
2
+
3
+ export function WCTitle() {
4
+ return <p className={styles.wcTitle}>Scan QR with your wallet</p>
5
+ }
@@ -0,0 +1,8 @@
1
+ .wcTitle {
2
+ margin-top: 24px;
3
+ font-size: 13px;
4
+ font-weight: 500;
5
+ color: var(--tcui-text-secondary);
6
+ margin-bottom: 0; /* force margin */
7
+ text-align: center;
8
+ }
@@ -0,0 +1,16 @@
1
+ import { WalletConnectButtonLogic } from '@trustwallet/connect-ui-logic/walletConnect'
2
+ import { WalletButton } from '../WalletButton'
3
+ import { WCTitle } from './components/WCTitle'
4
+ import { WCGrid } from './components/WCGrid'
5
+
6
+ export function WalletConnectButton() {
7
+ return (
8
+ <WalletConnectButtonLogic
9
+ components={{
10
+ title: WCTitle,
11
+ grid: WCGrid,
12
+ walletButton: WalletButton,
13
+ }}
14
+ />
15
+ )
16
+ }
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react'
2
+ import styles from './styles.module.css'
3
+
4
+ interface FooterDescriptionProps {
5
+ children: ReactNode
6
+ }
7
+
8
+ export function FooterDescription({ children }: FooterDescriptionProps) {
9
+ return <div className={styles.description}>{children}</div>
10
+ }
@@ -0,0 +1,6 @@
1
+ .description {
2
+ color: var(--tcui-text-secondary);
3
+ font-size: 14px;
4
+ line-height: 1.5;
5
+ text-align: center;
6
+ }
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react'
2
+ import styles from './styles.module.css'
3
+
4
+ interface FooterLinkProps {
5
+ onClick: () => void
6
+ children: ReactNode
7
+ }
8
+
9
+ export function FooterLink({ onClick, children }: FooterLinkProps) {
10
+ return (
11
+ <button className={styles.link} onClick={onClick}>
12
+ {children}
13
+ </button>
14
+ )
15
+ }
@@ -0,0 +1,20 @@
1
+ .link {
2
+ background: none;
3
+ border: none;
4
+ color: var(--tcui-accent);
5
+ font-size: 14px;
6
+ font-weight: 600;
7
+ line-height: 1.5;
8
+ cursor: pointer;
9
+ padding: 0;
10
+ text-decoration: none;
11
+ transition: opacity 0.2s ease;
12
+ }
13
+
14
+ .link:hover {
15
+ opacity: 0.8;
16
+ }
17
+
18
+ .link:active {
19
+ opacity: 0.6;
20
+ }
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react'
2
+ import styles from './styles.module.css'
3
+
4
+ interface FooterWrapperProps {
5
+ children: ReactNode
6
+ }
7
+
8
+ export function FooterWrapper({ children }: FooterWrapperProps) {
9
+ return <div className={styles.container}>{children}</div>
10
+ }
@@ -0,0 +1,21 @@
1
+ .container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ gap: 4px;
6
+ padding-top: 14px;
7
+ margin-top: auto;
8
+ position: relative;
9
+ border-top: 1px solid var(--tcui-border);
10
+ }
11
+
12
+ .container::before {
13
+ content: '';
14
+ position: absolute;
15
+ top: -33px;
16
+ left: 0;
17
+ right: 0;
18
+ height: 32px;
19
+ background: linear-gradient(to bottom, rgba(0, 0, 0, 0), var(--tcui-bg));
20
+ pointer-events: none;
21
+ }
@@ -0,0 +1,16 @@
1
+ import { FooterLogic } from '@trustwallet/connect-ui-logic'
2
+ import { FooterWrapper } from './components/FooterWrapper'
3
+ import { FooterDescription } from './components/FooterDescription'
4
+ import { FooterLink } from './components/FooterLink'
5
+
6
+ export function Footer() {
7
+ return (
8
+ <FooterLogic
9
+ components={{
10
+ wrapper: FooterWrapper,
11
+ description: FooterDescription,
12
+ link: FooterLink,
13
+ }}
14
+ />
15
+ )
16
+ }
@@ -0,0 +1,14 @@
1
+ export function BackIcon() {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ width="20"
8
+ height="20"
9
+ stroke="currentColor"
10
+ >
11
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 18l-6-6 6-6" />
12
+ </svg>
13
+ )
14
+ }
@@ -0,0 +1,17 @@
1
+ export function CheckIcon() {
2
+ return (
3
+ <svg
4
+ className="tcui-check-icon"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ width="14"
7
+ height="14"
8
+ viewBox="0 0 14 14"
9
+ fill="none"
10
+ >
11
+ <path
12
+ d="M6.66667 0C2.98667 0 0 2.98667 0 6.66667C0 10.3467 2.98667 13.3333 6.66667 13.3333C10.3467 13.3333 13.3333 10.3467 13.3333 6.66667C13.3333 2.98667 10.3467 0 6.66667 0ZM9.8 5.05333L6.45333 9.12667C6.34 9.26667 6.16667 9.35333 5.98667 9.36667C5.96667 9.37333 5.95333 9.37333 5.94 9.37333C5.77333 9.37333 5.61333 9.30667 5.48667 9.19333L3.59333 7.44667C3.32667 7.2 3.30667 6.77333 3.56 6.50667C3.80667 6.23333 4.22667 6.22 4.5 6.46667L5.87333 7.73333L8.77333 4.20667C9.00667 3.92 9.42667 3.88 9.70667 4.11333C9.99333 4.34667 10.0333 4.76667 9.8 5.05333Z"
13
+ fill="currentColor"
14
+ />
15
+ </svg>
16
+ )
17
+ }
@@ -0,0 +1,13 @@
1
+ export function ClearIcon() {
2
+ return (
3
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
4
+ <path
5
+ d="M15 5L5 15M5 5L15 15"
6
+ stroke="currentColor"
7
+ strokeWidth="2"
8
+ strokeLinecap="round"
9
+ strokeLinejoin="round"
10
+ />
11
+ </svg>
12
+ )
13
+ }
@@ -0,0 +1,14 @@
1
+ export function CloseIcon() {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ viewBox="0 0 24 24"
6
+ fill="none"
7
+ width="16"
8
+ height="16"
9
+ stroke="currentColor"
10
+ >
11
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 6l12 12M6 18L18 6" />
12
+ </svg>
13
+ )
14
+ }
@@ -0,0 +1,16 @@
1
+ export function CopyIcon() {
2
+ return (
3
+ <svg
4
+ className="tcui-copy-icon"
5
+ width="16"
6
+ height="16"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ strokeWidth="2"
11
+ >
12
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
13
+ <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
14
+ </svg>
15
+ )
16
+ }
@@ -0,0 +1,20 @@
1
+ export function SearchIcon() {
2
+ return (
3
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
4
+ <path
5
+ d="M7.33333 12.6667C10.2789 12.6667 12.6667 10.2789 12.6667 7.33333C12.6667 4.38781 10.2789 2 7.33333 2C4.38781 2 2 4.38781 2 7.33333C2 10.2789 4.38781 12.6667 7.33333 12.6667Z"
6
+ stroke="currentColor"
7
+ strokeWidth="1.5"
8
+ strokeLinecap="round"
9
+ strokeLinejoin="round"
10
+ />
11
+ <path
12
+ d="M14 14L11.1 11.1"
13
+ stroke="currentColor"
14
+ strokeWidth="1.5"
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ />
18
+ </svg>
19
+ )
20
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Spinner component that adapts to dark/light mode via CSS variables
3
+ * Uses --tcui-border and --tcui-accent from the theme
4
+ */
5
+ export function Spinner() {
6
+ return <span className="tcui-spinner" aria-hidden />
7
+ }
@@ -0,0 +1,44 @@
1
+ import { useState, type ChangeEvent } from 'react'
2
+ import styles from './styles.module.css'
3
+ import { SearchIcon } from '../../icons/SearchIcon'
4
+ import { CloseIcon } from '../../icons/CloseIcon'
5
+
6
+ interface SearchBarProps {
7
+ value: string
8
+ onChange: (value: string) => void
9
+ placeholder?: string
10
+ }
11
+
12
+ export function SearchBar({ value, onChange, placeholder = 'Search...' }: SearchBarProps) {
13
+ const [isFocused, setIsFocused] = useState(false)
14
+
15
+ const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
16
+ onChange(e.target.value)
17
+ }
18
+
19
+ const handleClear = () => {
20
+ onChange('')
21
+ }
22
+
23
+ return (
24
+ <div className={`${styles.searchBar} ${isFocused ? styles.focused : ''}`}>
25
+ <div className={styles.searchIcon}>
26
+ <SearchIcon />
27
+ </div>
28
+ <input
29
+ type="text"
30
+ value={value}
31
+ onChange={handleChange}
32
+ onFocus={() => setIsFocused(true)}
33
+ onBlur={() => setIsFocused(false)}
34
+ placeholder={placeholder}
35
+ className={styles.input}
36
+ />
37
+ {value && (
38
+ <button type="button" onClick={handleClear} className={styles.clearButton} aria-label="Clear search">
39
+ <CloseIcon />
40
+ </button>
41
+ )}
42
+ </div>
43
+ )
44
+ }
@@ -0,0 +1,62 @@
1
+ .searchBar {
2
+ position: relative;
3
+ display: flex;
4
+ align-items: center;
5
+ width: 100%;
6
+ background: var(--tcui-bg-secondary);
7
+ border: 1.4px solid var(--tcui-border);
8
+ border-radius: var(--tcui-radius-full);
9
+ padding: 0 12px;
10
+ height: 40px;
11
+ transition: all 0.2s ease;
12
+ }
13
+
14
+ .searchBar.focused {
15
+ border-color: var(--tcui-accent);
16
+ box-shadow: 0 0 0 3px rgba(72, 255, 145, 0.12), 0 0 3px rgba(72, 255, 145, 0.15);
17
+ }
18
+
19
+ [data-tcui-theme='light'] .searchBar.focused {
20
+ box-shadow: 0 0 0 3px rgba(5, 0, 255, 0.08), 0 0 3px rgba(5, 0, 255, 0.1);
21
+ }
22
+
23
+ .searchIcon {
24
+ flex-shrink: 0;
25
+ color: var(--tcui-text-secondary);
26
+ margin-right: 8px;
27
+ }
28
+
29
+ .input {
30
+ flex: 1;
31
+ border: none;
32
+ background: transparent;
33
+ outline: none;
34
+ font-size: 14px;
35
+ color: var(--tcui-text);
36
+ font-family: inherit;
37
+ }
38
+
39
+ .input::placeholder {
40
+ color: var(--tcui-text-secondary);
41
+ }
42
+
43
+ .clearButton {
44
+ flex-shrink: 0;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ width: 25px;
49
+ height: 25px;
50
+ border: none;
51
+ background: transparent;
52
+ color: var(--tcui-text-secondary);
53
+ cursor: pointer;
54
+ border-radius: var(--tcui-radius-sm);
55
+ transition: all 0.15s ease;
56
+ margin-left: 4px;
57
+ }
58
+
59
+ .clearButton:hover {
60
+ background: var(--tcui-bg-tertiary);
61
+ color: var(--tcui-text);
62
+ }
@@ -0,0 +1,15 @@
1
+ import { ReactNode, type RefObject } from 'react'
2
+ import styles from './styles.module.css'
3
+
4
+ interface ModalBodyProps {
5
+ children: ReactNode
6
+ bodyRef?: RefObject<HTMLDivElement | null>
7
+ }
8
+
9
+ export function ModalBody({ children, bodyRef }: ModalBodyProps) {
10
+ return (
11
+ <div ref={bodyRef} className={styles.body}>
12
+ {children}
13
+ </div>
14
+ )
15
+ }
@@ -0,0 +1,15 @@
1
+ .body {
2
+ padding: 20px;
3
+ overflow-y: auto;
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 16px;
7
+ }
8
+
9
+ /* Mobile styles */
10
+ @media (max-width: 768px) {
11
+ .body {
12
+ padding: 20px;
13
+ flex: 1;
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ import styles from './styles.module.css'
2
+
3
+ interface ModalErrorProps {
4
+ message: string
5
+ }
6
+
7
+ export function ModalError({ message }: ModalErrorProps) {
8
+ return <div className={styles.error}>Error: {message}</div>
9
+ }
@@ -0,0 +1,9 @@
1
+ .error {
2
+ background: rgba(240, 61, 62, 0.1);
3
+ border: 1px solid rgba(240, 61, 62, 0.3);
4
+ color: var(--tcui-danger);
5
+ border-radius: var(--tcui-radius);
6
+ padding: 12px 14px;
7
+ font-size: 14px;
8
+ line-height: 1.5;
9
+ }
@@ -0,0 +1,26 @@
1
+ import { BackIcon } from '../../icons/BackIcon'
2
+ import { CloseIcon } from '../../icons/CloseIcon'
3
+ import styles from './styles.module.css'
4
+
5
+ interface ModalHeaderProps {
6
+ title: string
7
+ showBack?: boolean
8
+ onBack?: () => void
9
+ onClose: () => void
10
+ }
11
+
12
+ export function ModalHeader({ title, showBack, onBack, onClose }: ModalHeaderProps) {
13
+ return (
14
+ <header className={styles.header}>
15
+ {showBack && onBack && (
16
+ <button type="button" className={styles.back} onClick={onBack} aria-label="Go back">
17
+ <BackIcon />
18
+ </button>
19
+ )}
20
+ <h2 className={styles.title}>{title}</h2>
21
+ <button type="button" className={styles.close} onClick={onClose} aria-label="Close modal">
22
+ <CloseIcon />
23
+ </button>
24
+ </header>
25
+ )
26
+ }
@@ -0,0 +1,92 @@
1
+ .header {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ padding: 20px;
6
+ gap: 12px;
7
+ border-bottom: 1px solid var(--tcui-border);
8
+ position: relative;
9
+ }
10
+
11
+ .title {
12
+ font-size: 18px;
13
+ font-weight: 600;
14
+ margin: 0;
15
+ letter-spacing: -0.01em;
16
+ line-height: 1.4;
17
+ text-align: center;
18
+ }
19
+
20
+ .close,
21
+ .back {
22
+ background: transparent;
23
+ border: none;
24
+ color: #9ca3af;
25
+ width: 32px;
26
+ height: 32px;
27
+ display: inline-flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ cursor: pointer;
31
+ border-radius: var(--tcui-radius-full);
32
+ transition: all 0.15s ease;
33
+ flex-shrink: 0;
34
+ padding: 0;
35
+ position: absolute;
36
+ }
37
+
38
+ .back {
39
+ left: 20px;
40
+ }
41
+
42
+ .close {
43
+ right: 20px;
44
+ }
45
+
46
+ [data-tcui-theme='light'] .close:hover,
47
+ [data-tcui-theme='light'] .back:hover {
48
+ background: #e5e7eb;
49
+ }
50
+
51
+ [data-tcui-theme='dark'] .close:hover,
52
+ [data-tcui-theme='dark'] .back:hover {
53
+ background: #1f2933;
54
+ }
55
+
56
+ /* Mobile styles */
57
+ @media (max-width: 768px) {
58
+ .header {
59
+ padding: 16px 20px;
60
+ border-bottom: 1px solid var(--tcui-border);
61
+ }
62
+
63
+ .header::before {
64
+ content: '';
65
+ position: absolute;
66
+ top: 8px;
67
+ left: 50%;
68
+ transform: translateX(-50%);
69
+ width: 32px;
70
+ height: 4px;
71
+ background: var(--tcui-border);
72
+ border-radius: 2px;
73
+ }
74
+
75
+ .title {
76
+ font-size: 16px;
77
+ }
78
+
79
+ .back,
80
+ .close {
81
+ width: 28px;
82
+ height: 28px;
83
+ }
84
+
85
+ .back {
86
+ left: 16px;
87
+ }
88
+
89
+ .close {
90
+ right: 16px;
91
+ }
92
+ }