@jaw.id/ui 0.0.3 → 0.0.4

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 (66) hide show
  1. package/cjs-error.cjs +3 -0
  2. package/dist/{ccip-BYa9WTP9.js → ccip-yRQNp5zr.js} +1 -1
  3. package/dist/{index-DBYNWeek.js → index-BcDVfcdq.js} +20832 -20511
  4. package/dist/index.js +1 -1
  5. package/package.json +8 -2
  6. package/.babelrc +0 -12
  7. package/CHANGELOG.md +0 -23
  8. package/components.json +0 -22
  9. package/postcss.config.cjs +0 -6
  10. package/src/components/ConnectDialog/index.tsx +0 -270
  11. package/src/components/ConnectDialog/types.ts +0 -34
  12. package/src/components/DefaultDialog/index.tsx +0 -99
  13. package/src/components/Eip712Dialog/index.tsx +0 -525
  14. package/src/components/Eip712Dialog/types.ts +0 -27
  15. package/src/components/FeeTokenSelector/index.tsx +0 -308
  16. package/src/components/OnboardingDialog/index.tsx +0 -317
  17. package/src/components/OnboardingDialog/types.ts +0 -43
  18. package/src/components/OrSeparator/index.tsx +0 -13
  19. package/src/components/PermissionDialog/index.tsx +0 -598
  20. package/src/components/PermissionDialog/types.ts +0 -77
  21. package/src/components/SignatureDialog/index.tsx +0 -180
  22. package/src/components/SignatureDialog/types.ts +0 -27
  23. package/src/components/SiweDialog/index.tsx +0 -231
  24. package/src/components/SiweDialog/types.ts +0 -34
  25. package/src/components/TransactionDialog/DecodedCalldata.tsx +0 -79
  26. package/src/components/TransactionDialog/index.tsx +0 -663
  27. package/src/components/TransactionDialog/types.ts +0 -54
  28. package/src/components/ui/accordion.tsx +0 -66
  29. package/src/components/ui/avatar.tsx +0 -53
  30. package/src/components/ui/button.tsx +0 -59
  31. package/src/components/ui/card.tsx +0 -92
  32. package/src/components/ui/checkbox.tsx +0 -32
  33. package/src/components/ui/dialog.tsx +0 -183
  34. package/src/components/ui/dropdown-menu.tsx +0 -258
  35. package/src/components/ui/form.tsx +0 -167
  36. package/src/components/ui/input.tsx +0 -60
  37. package/src/components/ui/label.tsx +0 -24
  38. package/src/components/ui/popover.tsx +0 -48
  39. package/src/components/ui/scroll-area.tsx +0 -58
  40. package/src/components/ui/select.tsx +0 -160
  41. package/src/components/ui/separator.tsx +0 -28
  42. package/src/components/ui/sheet.tsx +0 -169
  43. package/src/components/ui/spinner.tsx +0 -18
  44. package/src/components/ui/tabs.tsx +0 -69
  45. package/src/components/ui/tooltip.tsx +0 -61
  46. package/src/hooks/index.ts +0 -5
  47. package/src/hooks/useChainIconURI.tsx +0 -117
  48. package/src/hooks/useDecodedCalldata.ts +0 -128
  49. package/src/hooks/useFeeTokenPrice.tsx +0 -36
  50. package/src/hooks/useGasEstimation.ts +0 -474
  51. package/src/hooks/useIsMobile.ts +0 -36
  52. package/src/icons/index.tsx +0 -114
  53. package/src/index.ts +0 -19
  54. package/src/lib/utils.ts +0 -6
  55. package/src/react/ReactUIHandler.tsx +0 -3004
  56. package/src/react/index.ts +0 -2
  57. package/src/styles.css +0 -210
  58. package/src/utils/formatAddress.ts +0 -24
  59. package/src/utils/index.ts +0 -4
  60. package/src/utils/justaNameInstance.ts +0 -25
  61. package/src/utils/tokenBalance.ts +0 -41
  62. package/src/utils/tokenPrice.ts +0 -58
  63. package/tailwind.config.js +0 -130
  64. package/tsconfig.json +0 -19
  65. package/tsconfig.lib.json +0 -43
  66. package/vite.config.ts +0 -45
@@ -1,525 +0,0 @@
1
- 'use client'
2
-
3
- import { Button } from "../ui/button";
4
- import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../ui/accordion";
5
- import { DefaultDialog } from "../DefaultDialog";
6
- import { Eip712DialogProps } from "./types";
7
- import { useIsMobile } from "../../hooks";
8
- import { getJustaNameInstance, getDisplayAddress } from "../../utils";
9
- import { useState, useEffect, useMemo, useRef } from "react";
10
-
11
- // EIP-712 TypedData structure
12
- interface TypedData {
13
- types: Record<string, Array<{ name: string; type: string }>>;
14
- primaryType: string;
15
- domain: Record<string, unknown>;
16
- message: Record<string, unknown>;
17
- }
18
-
19
- const isObject = (value: unknown): value is Record<string, unknown> => {
20
- return typeof value === 'object' && value !== null && !Array.isArray(value);
21
- };
22
-
23
- const isArray = (value: unknown): value is unknown[] => {
24
- return Array.isArray(value);
25
- };
26
-
27
- const getValueColor = (value: unknown): string => {
28
- if (typeof value === 'string') return 'text-foreground';
29
- if (typeof value === 'number') return 'text-blue-600 dark:text-blue-400';
30
- if (typeof value === 'boolean') return 'text-purple-600 dark:text-purple-400';
31
- if (value === null || value === undefined) return 'text-gray-500 dark:text-gray-400';
32
- return 'text-foreground';
33
- };
34
-
35
- const formatPrimitiveValue = (value: unknown): { text: string; color: string } => {
36
- if (value === null) return { text: 'null', color: getValueColor(null) };
37
- if (value === undefined) return { text: 'undefined', color: getValueColor(undefined) };
38
- if (typeof value === 'boolean') return { text: String(value), color: getValueColor(value) };
39
- if (typeof value === 'number') return { text: String(value), color: getValueColor(value) };
40
- if (typeof value === 'string') return { text: `"${value}"`, color: getValueColor(value) };
41
- return { text: JSON.stringify(value), color: 'text-foreground' };
42
- };
43
-
44
- // Component for rendering a single property line
45
- const PropertyLine = ({
46
- propertyKey,
47
- value,
48
- isLast,
49
- depth
50
- }: {
51
- propertyKey: string;
52
- value: unknown;
53
- isLast: boolean;
54
- depth: number;
55
- }) => {
56
- const formatted = formatPrimitiveValue(value);
57
- const paddingLeft = depth * 16; // 16px per depth level
58
-
59
- return (
60
- <div
61
- className="flex items-start gap-1 py-0.5 font-mono text-sm"
62
- style={{ paddingLeft: `${paddingLeft}px` }}
63
- >
64
- <span className="text-muted-foreground">"{propertyKey}":</span>
65
- <span className={formatted.color}>{formatted.text}</span>
66
- {!isLast && <span className="text-muted-foreground">,</span>}
67
- </div>
68
- );
69
- };
70
-
71
- // Recursive component to render nested objects
72
- const NestedDataView = ({
73
- data,
74
- depth = 0,
75
- parentKey,
76
- isLast = true
77
- }: {
78
- data: unknown;
79
- depth?: number;
80
- parentKey?: string;
81
- isLast?: boolean;
82
- }) => {
83
- const paddingLeft = depth * 16;
84
-
85
- if (isObject(data)) {
86
- const entries = Object.entries(data);
87
- const accordionId = `${parentKey || 'root'}-${depth}`;
88
-
89
- if (depth === 0) {
90
- return (
91
- <div className="w-full font-mono text-sm">
92
- <div className="flex flex-col">
93
- {entries.map(([key, value], index) => {
94
- const isLastEntry = index === entries.length - 1;
95
-
96
- if (isObject(value) || isArray(value)) {
97
- return (
98
- <NestedDataView
99
- key={key}
100
- data={value}
101
- depth={depth + 1}
102
- parentKey={key}
103
- isLast={isLastEntry}
104
- />
105
- );
106
- }
107
-
108
- return (
109
- <PropertyLine
110
- key={key}
111
- propertyKey={key}
112
- value={value}
113
- isLast={isLastEntry}
114
- depth={depth}
115
- />
116
- );
117
- })}
118
- </div>
119
- </div>
120
- );
121
- }
122
-
123
- return (
124
- <div className="w-full font-mono text-sm">
125
- <Accordion type="multiple" className="w-full" defaultValue={[]}>
126
- <AccordionItem value={accordionId} className="border-none">
127
- <div style={{ paddingLeft: `${paddingLeft}px` }}>
128
- <AccordionTrigger className="py-0.5 hover:no-underline hover:opacity-70 transition-opacity cursor-pointer [&>svg]:hidden group">
129
- <span className="flex items-center gap-0.5">
130
- {parentKey && <span className="text-muted-foreground">"{parentKey}":</span>}
131
- <span className="text-muted-foreground group-data-[state=closed]:inline hidden">{' {...}'}</span>
132
- <span className="text-muted-foreground group-data-[state=open]:inline hidden">{' {'}</span>
133
- {!isLast && <span className="text-muted-foreground group-data-[state=closed]:inline hidden">,</span>}
134
- </span>
135
- </AccordionTrigger>
136
- </div>
137
- <AccordionContent className="pb-0 pt-0">
138
- <div className="flex flex-col">
139
- {entries.map(([key, value], index) => {
140
- const isLastEntry = index === entries.length - 1;
141
-
142
- if (isObject(value) || isArray(value)) {
143
- return (
144
- <NestedDataView
145
- key={key}
146
- data={value}
147
- depth={depth + 1}
148
- parentKey={key}
149
- isLast={isLastEntry}
150
- />
151
- );
152
- }
153
-
154
- return (
155
- <PropertyLine
156
- key={key}
157
- propertyKey={key}
158
- value={value}
159
- isLast={isLastEntry}
160
- depth={depth + 1}
161
- />
162
- );
163
- })}
164
- <div
165
- className="py-0.5 text-muted-foreground"
166
- style={{ paddingLeft: `${paddingLeft}px` }}
167
- >
168
- <span>{'}'}</span>
169
- {!isLast && <span>,</span>}
170
- </div>
171
- </div>
172
- </AccordionContent>
173
- </AccordionItem>
174
- </Accordion>
175
- </div>
176
- );
177
- }
178
-
179
- if (isArray(data)) {
180
- const accordionId = `${parentKey || 'array'}-${depth}`;
181
-
182
- if (depth === 0) {
183
- return (
184
- <div className="w-full font-mono text-sm">
185
- <div className="flex flex-col">
186
- {data.map((item, index) => {
187
- const isLastEntry = index === data.length - 1;
188
- const indexKey = `[${index}]`;
189
-
190
- if (isObject(item) || isArray(item)) {
191
- return (
192
- <div key={index}>
193
- <div
194
- className="py-0.5 text-muted-foreground font-mono text-sm"
195
- style={{ paddingLeft: `${depth * 16}px` }}
196
- >
197
- {indexKey}:
198
- </div>
199
- <NestedDataView
200
- data={item}
201
- depth={depth + 1}
202
- isLast={isLastEntry}
203
- />
204
- </div>
205
- );
206
- }
207
-
208
- const formatted = formatPrimitiveValue(item);
209
- return (
210
- <div
211
- key={index}
212
- className="flex items-start gap-1 py-0.5 font-mono text-sm"
213
- style={{ paddingLeft: `${depth * 16}px` }}
214
- >
215
- <span className="text-muted-foreground">{indexKey}:</span>
216
- <span className={formatted.color}>{formatted.text}</span>
217
- {!isLastEntry && <span className="text-muted-foreground">,</span>}
218
- </div>
219
- );
220
- })}
221
- </div>
222
- </div>
223
- );
224
- }
225
-
226
- return (
227
- <div className="w-full font-mono text-sm">
228
- <Accordion type="multiple" className="w-full" defaultValue={[]}>
229
- <AccordionItem value={accordionId} className="border-none">
230
- <div style={{ paddingLeft: `${paddingLeft}px` }}>
231
- <AccordionTrigger className="py-0.5 hover:no-underline hover:opacity-70 transition-opacity cursor-pointer [&>svg]:hidden group">
232
- <span className="flex items-center gap-0.5">
233
- {parentKey && <span className="text-muted-foreground">"{parentKey}":</span>}
234
- <span className="text-muted-foreground group-data-[state=closed]:inline hidden">{' [...]'}</span>
235
- <span className="text-muted-foreground group-data-[state=open]:inline hidden">{' ['}</span>
236
- {!isLast && <span className="text-muted-foreground group-data-[state=closed]:inline hidden">,</span>}
237
- </span>
238
- </AccordionTrigger>
239
- </div>
240
- <AccordionContent className="pb-0 pt-0">
241
- <div className="flex flex-col">
242
- {data.map((item, index) => {
243
- const isLastEntry = index === data.length - 1;
244
- const indexKey = `[${index}]`;
245
-
246
- if (isObject(item) || isArray(item)) {
247
- return (
248
- <div key={index}>
249
- <div
250
- className="py-0.5 text-muted-foreground font-mono text-sm"
251
- style={{ paddingLeft: `${(depth + 1) * 16}px` }}
252
- >
253
- {indexKey}:
254
- </div>
255
- <NestedDataView
256
- data={item}
257
- depth={depth + 2}
258
- isLast={isLastEntry}
259
- />
260
- </div>
261
- );
262
- }
263
-
264
- const formatted = formatPrimitiveValue(item);
265
- return (
266
- <div
267
- key={index}
268
- className="flex items-start gap-1 py-0.5 font-mono text-sm"
269
- style={{ paddingLeft: `${(depth + 1) * 16}px` }}
270
- >
271
- <span className="text-muted-foreground">{indexKey}:</span>
272
- <span className={formatted.color}>{formatted.text}</span>
273
- {!isLastEntry && <span className="text-muted-foreground">,</span>}
274
- </div>
275
- );
276
- })}
277
- <div
278
- className="py-0.5 text-muted-foreground"
279
- style={{ paddingLeft: `${paddingLeft}px` }}
280
- >
281
- <span>{']'}</span>
282
- {!isLast && <span>,</span>}
283
- </div>
284
- </div>
285
- </AccordionContent>
286
- </AccordionItem>
287
- </Accordion>
288
- </div>
289
- );
290
- }
291
-
292
- const formatted = formatPrimitiveValue(data);
293
- return (
294
- <div className="py-0.5 font-mono text-sm" style={{ paddingLeft: `${paddingLeft}px` }}>
295
- <span className={formatted.color}>{formatted.text}</span>
296
- </div>
297
- );
298
- };
299
-
300
- export const Eip712Dialog = ({
301
- open,
302
- onOpenChange,
303
- typedDataJson,
304
- origin,
305
- timestamp,
306
- accountAddress,
307
- chainName,
308
- chainId,
309
- chainIcon,
310
- mainnetRpcUrl,
311
- onSign,
312
- onCancel,
313
- isProcessing,
314
- signatureStatus,
315
- canSign,
316
- }: Eip712DialogProps) => {
317
- // Ref for scrollable container
318
- const scrollableRef = useRef<HTMLDivElement>(null);
319
-
320
- const isMobile = useIsMobile();
321
- const [resolvedAddress, setResolvedAddress] = useState<string | null>(null);
322
-
323
- // Parse typed data
324
- const typedData = useMemo(() => {
325
- try {
326
- return JSON.parse(typedDataJson) as TypedData;
327
- } catch (error) {
328
- console.error('Failed to parse typed data:', error);
329
- return null;
330
- }
331
- }, [typedDataJson]);
332
-
333
- // Resolve account address to human-readable name
334
- useEffect(() => {
335
- if (accountAddress && chainId) {
336
- const justaName = getJustaNameInstance(mainnetRpcUrl);
337
- justaName.subnames.reverseResolve({
338
- address: accountAddress as `0x${string}`,
339
- chainId: chainId,
340
- }).then((result) => {
341
- if (result) {
342
- setResolvedAddress(result);
343
- }
344
- }).catch(() => {
345
- // Silently fail if resolution fails
346
- });
347
- }
348
- }, [accountAddress, chainId]);
349
-
350
- // Handle wheel events for smooth scrolling over JSON content
351
- useEffect(() => {
352
- if (!open) return;
353
-
354
- let cleanupFn: (() => void) | null = null;
355
-
356
- // Use a small delay to ensure the DOM is ready
357
- const timer = setTimeout(() => {
358
- const scrollable = scrollableRef.current;
359
- if (!scrollable) return;
360
-
361
- const handleWheel = (e: WheelEvent) => {
362
- e.preventDefault();
363
- scrollable.scrollTop += e.deltaY;
364
- };
365
-
366
- scrollable.addEventListener('wheel', handleWheel, { passive: false });
367
-
368
- cleanupFn = () => {
369
- scrollable.removeEventListener('wheel', handleWheel);
370
- };
371
- }, 100);
372
-
373
- return () => {
374
- clearTimeout(timer);
375
- if (cleanupFn) cleanupFn();
376
- };
377
- }, [open]);
378
-
379
- // Get display address - use resolved name or formatted address
380
- const displayAddress = getDisplayAddress(resolvedAddress, accountAddress || '');
381
-
382
- // Format origin to display only domain (remove protocol)
383
- const formatOrigin = (url: string) => {
384
- try {
385
- const urlObj = new URL(url.startsWith('http') ? url : `https://${url}`);
386
- return urlObj.hostname.replace('www.', '');
387
- } catch {
388
- return origin;
389
- }
390
- };
391
-
392
- // Get contract address from domain
393
- const contractAddress = typedData?.domain?.verifyingContract as string | undefined;
394
- const domainName = typedData?.domain?.name as string | undefined;
395
-
396
- return (
397
- <DefaultDialog
398
- open={open}
399
- onOpenChange={!isProcessing ? onOpenChange : undefined}
400
- header={
401
- <div className="flex flex-col gap-2.5 p-3.5">
402
- <p className="text-xs font-bold text-muted-foreground leading-[100%]">
403
- {timestamp.toLocaleDateString('en-US', {
404
- weekday: 'long',
405
- day: 'numeric',
406
- month: 'long'
407
- })} at {timestamp.toLocaleTimeString('en-US', {
408
- hour: '2-digit',
409
- minute: '2-digit',
410
- second: '2-digit',
411
- timeZoneName: 'short'
412
- })}
413
- </p>
414
- <p className="text-[30px] font-normal leading-[100%] text-foreground">
415
- Review
416
- </p>
417
- <p className="text-xs font-normal leading-[100%] text-foreground">{displayAddress}</p>
418
- </div>
419
- }
420
- contentStyle={isMobile ? {
421
- width: '100%',
422
- height: '100%',
423
- maxWidth: 'none',
424
- maxHeight: 'none',
425
- overflowY: 'auto',
426
- } : {
427
- width: '500px',
428
- minWidth: '500px',
429
- }}
430
- >
431
- <div className="flex flex-col gap-6 justify-between max-md:h-full">
432
- {/* Main Content - Typed Data Tree View */}
433
- <div className="flex flex-col gap-3 max-md:flex-1 max-h-[60vh] overflow-y-auto min-h-0">
434
- {typedData ? (
435
- <div ref={scrollableRef} className="max-h-[50vh] flex flex-1 overflow-y-auto bg-muted/30 dark:bg-muted/10 rounded-[6px] p-3 border border-border">
436
- {/* Combine domain and message into single tree */}
437
- <NestedDataView
438
- data={
439
- typedData
440
- }
441
- depth={0}
442
- />
443
- </div>
444
- ) : (
445
- <div className="p-4 bg-red-50 border border-red-200 rounded-[6px]">
446
- <p className="text-sm text-red-600">Failed to parse typed data</p>
447
- </div>
448
- )}
449
-
450
- {/* URL and Domain Information */}
451
- <div className="flex flex-row justify-between items-center gap-2.5 p-3.5 border border-border rounded-[6px] max-md:mt-auto">
452
- <div className="flex flex-col text-foreground gap-0.5 min-w-0 flex-1">
453
- <p className="text-xs font-bold leading-[133%]">URL</p>
454
- <p className="text-base font-normal leading-[150%] truncate">
455
- {formatOrigin(origin)}
456
- </p>
457
- </div>
458
- <div className="w-[1px] rounded-full bg-border h-full flex-shrink-0 min-h-[50px]" />
459
- <div className="flex flex-col text-foreground gap-0.5 min-w-0 flex-1">
460
- <p className="text-xs font-bold leading-[133%]">Domain</p>
461
- <p className="text-base font-normal leading-[150%] truncate">
462
- {domainName || formatOrigin(origin)}
463
- </p>
464
- </div>
465
- </div>
466
-
467
- {/* Contract Address if available */}
468
- {contractAddress && (
469
- <div className="flex flex-row justify-between items-center gap-2.5 p-3.5 border border-border rounded-[6px]">
470
- <div className="flex flex-col text-foreground gap-0.5 min-w-0 flex-1">
471
- <p className="text-xs font-bold leading-[133%]">Contract</p>
472
- <p className="text-base font-normal leading-[150%] truncate">
473
- {contractAddress}
474
- </p>
475
- </div>
476
- {chainName && (
477
- <>
478
- <div className="w-[1px] rounded-full bg-border h-full flex-shrink-0 min-h-[50px]" />
479
- <div className="flex flex-col text-foreground gap-0.5 min-w-0 flex-1">
480
- <p className="text-xs font-bold leading-[133%]">Network</p>
481
- <div className="flex flex-row items-center gap-1 min-w-0">
482
- {chainIcon && <div className="w-6 h-6 flex-shrink-0">{chainIcon}</div>}
483
- <p className="text-base font-normal leading-[150%] truncate">{chainName}</p>
484
- </div>
485
- </div>
486
- </>
487
- )}
488
- </div>
489
- )}
490
-
491
- {/* Status Message */}
492
- {signatureStatus && (
493
- <div className={`text-sm p-3 rounded-lg ${signatureStatus.includes('Error') ? 'bg-red-50 text-red-600' :
494
- signatureStatus.includes('successfully') ? 'bg-green-50 text-green-600' :
495
- 'bg-blue-50 text-blue-600'
496
- }`}>
497
- {signatureStatus}
498
- </div>
499
- )}
500
- </div>
501
-
502
- {/* Action Buttons */}
503
- <div className="flex gap-3 p-3.5 flex-shrink-0">
504
- <Button
505
- variant="outline"
506
- onClick={onCancel}
507
- disabled={isProcessing}
508
- className="flex-1"
509
- >
510
- Cancel
511
- </Button>
512
- <Button
513
- onClick={onSign}
514
- disabled={!canSign}
515
- className="flex-1"
516
- >
517
- {isProcessing ? 'Processing...' : 'Sign'}
518
- </Button>
519
- </div>
520
- </div>
521
- </DefaultDialog>
522
- )
523
- }
524
-
525
- export * from './types';
@@ -1,27 +0,0 @@
1
- import { JSX } from "react";
2
- export interface Eip712DialogProps {
3
- open: boolean;
4
- onOpenChange: (open: boolean) => void;
5
-
6
- // EIP-712 typed data (JSON string)
7
- typedDataJson: string;
8
- origin: string;
9
- timestamp: Date;
10
-
11
- accountAddress?: string;
12
- chainName?: string;
13
- chainId?: number;
14
- chainIcon?: JSX.Element;
15
-
16
- // Actions
17
- onSign: () => Promise<void>;
18
- onCancel: () => void;
19
-
20
- // Status
21
- isProcessing: boolean;
22
- signatureStatus: string;
23
- canSign: boolean;
24
-
25
- // RPC configuration
26
- mainnetRpcUrl: string;
27
- }