@openzeppelin/ui-renderer 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/index.cjs +695 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +81 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +696 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AlertCircle, AlertTriangle, CheckCircle, CheckCircle2, ExternalLink, FileText, Info, Key, Loader2, Minimize2, Network, RefreshCw, Settings, Shield, User, Users, XCircle } from "lucide-react";
|
|
1
|
+
import { AlertCircle, AlertTriangle, BookUser, Check, CheckCircle, CheckCircle2, Download, ExternalLink, FileText, Filter, Info, Key, Loader2, Minimize2, Network, Pencil, Plus, RefreshCw, Search, Settings, Shield, Trash2, Upload, User, Users, X, XCircle } from "lucide-react";
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { FormProvider, useForm, useWatch } from "react-hook-form";
|
|
4
|
-
import { appConfigService, cn, logger, rateLimitedBatch, sanitizeHtml, userNetworkServiceConfigService, userRpcConfigService } from "@openzeppelin/ui-utils";
|
|
5
|
-
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AddressDisplay, AddressField, Alert, AlertDescription, AlertTitle, AmountField, ArrayField, ArrayObjectField, BigIntField, BooleanField, Button, BytesField, Card, CardContent, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, EmptyState, EnumField, FileUploadField, LoadingButton, MapField, NetworkStatusBadge, NumberField, ObjectField, PasswordField, RadioField, RelayerDetailsCard, SelectField, SelectGroupedField, Tabs, TabsContent, TabsList, TabsTrigger, TextAreaField, TextField, UrlField, ViewContractStateButton, useNetworkErrors } from "@openzeppelin/ui-components";
|
|
4
|
+
import { appConfigService, cn, filterEnabledServiceForms, logger, rateLimitedBatch, sanitizeHtml, userNetworkServiceConfigService, userRpcConfigService } from "@openzeppelin/ui-utils";
|
|
5
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AddressDisplay, AddressField, AddressLabelProvider, Alert, AlertDescription, AlertTitle, AmountField, ArrayField, ArrayObjectField, BigIntField, BooleanField, Button, BytesField, Card, CardContent, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, EmptyState, EnumField, FileUploadField, Input, Label, LoadingButton, MapField, NetworkIcon, NetworkSelector, NetworkStatusBadge, NumberField, ObjectField, OverflowMenu, PasswordField, Popover, PopoverAnchor, PopoverContent, RadioField, RelayerDetailsCard, SelectField, SelectGroupedField, Tabs, TabsContent, TabsList, TabsTrigger, TextAreaField, TextField, UrlField, ViewContractStateButton, useNetworkErrors } from "@openzeppelin/ui-components";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { WalletConnectionUI, useWalletState } from "@openzeppelin/ui-react";
|
|
8
8
|
import { CodeEditorField } from "@openzeppelin/ui-components/code-editor";
|
|
@@ -1414,6 +1414,697 @@ function TransactionForm({ schema, contractSchema, adapter, isWalletConnected =
|
|
|
1414
1414
|
});
|
|
1415
1415
|
}
|
|
1416
1416
|
|
|
1417
|
+
//#endregion
|
|
1418
|
+
//#region src/components/AddressBookWidget/AddAliasDialog.tsx
|
|
1419
|
+
/** Dialog for creating a new address alias entry. */
|
|
1420
|
+
function AddAliasDialog({ open, onOpenChange, onSave, currentNetworkId, adapter: defaultAdapter, resolveAdapter, addressPlaceholder: defaultPlaceholder, resolveAddressPlaceholder, resolveNetwork, networks }) {
|
|
1421
|
+
const [saving, setSaving] = useState(false);
|
|
1422
|
+
const initialNetwork = useMemo(() => currentNetworkId && resolveNetwork ? resolveNetwork(currentNetworkId) : void 0, [currentNetworkId, resolveNetwork]);
|
|
1423
|
+
const [selectedNetwork, setSelectedNetwork] = useState(initialNetwork ?? null);
|
|
1424
|
+
const [activeAdapter, setActiveAdapter] = useState(defaultAdapter);
|
|
1425
|
+
const [activePlaceholder, setActivePlaceholder] = useState(defaultPlaceholder);
|
|
1426
|
+
useEffect(() => {
|
|
1427
|
+
if (open) {
|
|
1428
|
+
setSelectedNetwork(initialNetwork ?? null);
|
|
1429
|
+
setActiveAdapter(defaultAdapter);
|
|
1430
|
+
setActivePlaceholder(defaultPlaceholder);
|
|
1431
|
+
}
|
|
1432
|
+
}, [
|
|
1433
|
+
open,
|
|
1434
|
+
initialNetwork,
|
|
1435
|
+
defaultAdapter,
|
|
1436
|
+
defaultPlaceholder
|
|
1437
|
+
]);
|
|
1438
|
+
const { control, handleSubmit, reset, trigger, formState } = useForm({
|
|
1439
|
+
defaultValues: {
|
|
1440
|
+
address: "",
|
|
1441
|
+
alias: ""
|
|
1442
|
+
},
|
|
1443
|
+
mode: "onChange"
|
|
1444
|
+
});
|
|
1445
|
+
const handleNetworkChange = useCallback(async (network) => {
|
|
1446
|
+
setSelectedNetwork(network);
|
|
1447
|
+
if (resolveAddressPlaceholder) setActivePlaceholder(resolveAddressPlaceholder(network));
|
|
1448
|
+
if (resolveAdapter) {
|
|
1449
|
+
setActiveAdapter(await resolveAdapter(network));
|
|
1450
|
+
trigger("address");
|
|
1451
|
+
}
|
|
1452
|
+
}, [
|
|
1453
|
+
resolveAdapter,
|
|
1454
|
+
resolveAddressPlaceholder,
|
|
1455
|
+
trigger
|
|
1456
|
+
]);
|
|
1457
|
+
const canSubmit = formState.isValid && !saving;
|
|
1458
|
+
const onSubmit = useCallback(async (data) => {
|
|
1459
|
+
const networkId = selectedNetwork?.id;
|
|
1460
|
+
setSaving(true);
|
|
1461
|
+
try {
|
|
1462
|
+
await onSave({
|
|
1463
|
+
address: data.address.trim(),
|
|
1464
|
+
alias: data.alias.trim(),
|
|
1465
|
+
networkId
|
|
1466
|
+
});
|
|
1467
|
+
reset();
|
|
1468
|
+
onOpenChange(false);
|
|
1469
|
+
} finally {
|
|
1470
|
+
setSaving(false);
|
|
1471
|
+
}
|
|
1472
|
+
}, [
|
|
1473
|
+
onOpenChange,
|
|
1474
|
+
onSave,
|
|
1475
|
+
reset,
|
|
1476
|
+
selectedNetwork
|
|
1477
|
+
]);
|
|
1478
|
+
const handleOpenChange = useCallback((nextOpen) => {
|
|
1479
|
+
if (!nextOpen) {
|
|
1480
|
+
reset();
|
|
1481
|
+
setSelectedNetwork(initialNetwork ?? null);
|
|
1482
|
+
setActiveAdapter(defaultAdapter);
|
|
1483
|
+
setActivePlaceholder(defaultPlaceholder);
|
|
1484
|
+
}
|
|
1485
|
+
onOpenChange(nextOpen);
|
|
1486
|
+
}, [
|
|
1487
|
+
defaultAdapter,
|
|
1488
|
+
defaultPlaceholder,
|
|
1489
|
+
initialNetwork,
|
|
1490
|
+
onOpenChange,
|
|
1491
|
+
reset
|
|
1492
|
+
]);
|
|
1493
|
+
const hasNetworkSelection = networks && networks.length > 0;
|
|
1494
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
1495
|
+
open,
|
|
1496
|
+
onOpenChange: handleOpenChange,
|
|
1497
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
1498
|
+
className: "max-w-md",
|
|
1499
|
+
children: [
|
|
1500
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: "Add Alias" }), /* @__PURE__ */ jsx(DialogDescription, { children: "Create a human-readable name for an address." })] }),
|
|
1501
|
+
/* @__PURE__ */ jsxs("form", {
|
|
1502
|
+
id: "add-alias-form",
|
|
1503
|
+
onSubmit: handleSubmit(onSubmit),
|
|
1504
|
+
className: "space-y-4",
|
|
1505
|
+
children: [
|
|
1506
|
+
hasNetworkSelection && /* @__PURE__ */ jsxs("div", {
|
|
1507
|
+
className: "space-y-2",
|
|
1508
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
1509
|
+
htmlFor: "alias-network",
|
|
1510
|
+
children: "Network"
|
|
1511
|
+
}), /* @__PURE__ */ jsx(NetworkSelector, {
|
|
1512
|
+
networks,
|
|
1513
|
+
selectedNetwork,
|
|
1514
|
+
onSelectNetwork: handleNetworkChange,
|
|
1515
|
+
getNetworkLabel: (n) => n.name,
|
|
1516
|
+
getNetworkId: (n) => n.id,
|
|
1517
|
+
getNetworkIcon: (n) => /* @__PURE__ */ jsx(NetworkIcon, { network: n }),
|
|
1518
|
+
getNetworkType: (n) => n.type,
|
|
1519
|
+
groupByEcosystem: true,
|
|
1520
|
+
getEcosystem: (n) => n.ecosystem.toUpperCase(),
|
|
1521
|
+
placeholder: "Select network…"
|
|
1522
|
+
})]
|
|
1523
|
+
}),
|
|
1524
|
+
/* @__PURE__ */ jsx(AddressField, {
|
|
1525
|
+
id: "new-alias-address",
|
|
1526
|
+
name: "address",
|
|
1527
|
+
label: "Address",
|
|
1528
|
+
placeholder: activePlaceholder,
|
|
1529
|
+
control,
|
|
1530
|
+
validation: { required: true },
|
|
1531
|
+
adapter: activeAdapter
|
|
1532
|
+
}),
|
|
1533
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
1534
|
+
id: "new-alias-name",
|
|
1535
|
+
name: "alias",
|
|
1536
|
+
label: "Alias",
|
|
1537
|
+
placeholder: "e.g. Treasury",
|
|
1538
|
+
control,
|
|
1539
|
+
validation: { required: true }
|
|
1540
|
+
})
|
|
1541
|
+
]
|
|
1542
|
+
}),
|
|
1543
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
1544
|
+
variant: "outline",
|
|
1545
|
+
size: "sm",
|
|
1546
|
+
onClick: () => handleOpenChange(false),
|
|
1547
|
+
children: "Cancel"
|
|
1548
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
1549
|
+
type: "submit",
|
|
1550
|
+
form: "add-alias-form",
|
|
1551
|
+
size: "sm",
|
|
1552
|
+
disabled: !canSubmit,
|
|
1553
|
+
children: [/* @__PURE__ */ jsx(Plus, {
|
|
1554
|
+
className: "mr-1.5 h-3.5 w-3.5",
|
|
1555
|
+
"aria-hidden": "true"
|
|
1556
|
+
}), saving ? "Adding…" : "Add"]
|
|
1557
|
+
})] })
|
|
1558
|
+
]
|
|
1559
|
+
})
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
//#endregion
|
|
1564
|
+
//#region src/components/AddressBookWidget/AliasRow.tsx
|
|
1565
|
+
/** Single row in the Address Book widget displaying an alias record. */
|
|
1566
|
+
function AliasRow({ alias, onSave, onRemove, resolveNetwork, resolveExplorerUrl }) {
|
|
1567
|
+
const [editing, setEditing] = useState(false);
|
|
1568
|
+
const [editValue, setEditValue] = useState(alias.alias);
|
|
1569
|
+
const [busy, setBusy] = useState(false);
|
|
1570
|
+
const network = useMemo(() => alias.networkId && resolveNetwork ? resolveNetwork(alias.networkId) : void 0, [alias.networkId, resolveNetwork]);
|
|
1571
|
+
const explorerUrl = useMemo(() => resolveExplorerUrl?.(alias.address, alias.networkId), [
|
|
1572
|
+
alias.address,
|
|
1573
|
+
alias.networkId,
|
|
1574
|
+
resolveExplorerUrl
|
|
1575
|
+
]);
|
|
1576
|
+
const handleEdit = useCallback(() => {
|
|
1577
|
+
setEditValue(alias.alias);
|
|
1578
|
+
setEditing(true);
|
|
1579
|
+
}, [alias.alias]);
|
|
1580
|
+
const handleCancel = useCallback(() => {
|
|
1581
|
+
setEditing(false);
|
|
1582
|
+
}, []);
|
|
1583
|
+
const handleSave = useCallback(async () => {
|
|
1584
|
+
const trimmed = editValue.trim();
|
|
1585
|
+
if (!trimmed || trimmed === alias.alias) {
|
|
1586
|
+
setEditing(false);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
setBusy(true);
|
|
1590
|
+
try {
|
|
1591
|
+
await onSave({
|
|
1592
|
+
address: alias.address,
|
|
1593
|
+
alias: trimmed,
|
|
1594
|
+
networkId: alias.networkId
|
|
1595
|
+
});
|
|
1596
|
+
setEditing(false);
|
|
1597
|
+
} finally {
|
|
1598
|
+
setBusy(false);
|
|
1599
|
+
}
|
|
1600
|
+
}, [
|
|
1601
|
+
alias,
|
|
1602
|
+
editValue,
|
|
1603
|
+
onSave
|
|
1604
|
+
]);
|
|
1605
|
+
const handleRemove = useCallback(async () => {
|
|
1606
|
+
setBusy(true);
|
|
1607
|
+
try {
|
|
1608
|
+
await onRemove(alias.id);
|
|
1609
|
+
} finally {
|
|
1610
|
+
setBusy(false);
|
|
1611
|
+
}
|
|
1612
|
+
}, [alias.id, onRemove]);
|
|
1613
|
+
const handleKeyDown = useCallback((e) => {
|
|
1614
|
+
if (e.key === "Enter") handleSave();
|
|
1615
|
+
if (e.key === "Escape") handleCancel();
|
|
1616
|
+
}, [handleSave, handleCancel]);
|
|
1617
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1618
|
+
className: "flex items-center gap-3 rounded-md border p-3",
|
|
1619
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1620
|
+
className: "min-w-0 flex-1",
|
|
1621
|
+
children: [editing ? /* @__PURE__ */ jsx("div", {
|
|
1622
|
+
className: "mb-2 max-w-sm",
|
|
1623
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
1624
|
+
value: editValue,
|
|
1625
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
1626
|
+
onKeyDown: handleKeyDown,
|
|
1627
|
+
className: "h-8 text-base font-semibold",
|
|
1628
|
+
autoFocus: true,
|
|
1629
|
+
disabled: busy
|
|
1630
|
+
})
|
|
1631
|
+
}) : /* @__PURE__ */ jsx("h3", {
|
|
1632
|
+
className: "mb-2.5 truncate text-base font-semibold text-foreground",
|
|
1633
|
+
children: alias.alias
|
|
1634
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1635
|
+
className: "flex items-center gap-2",
|
|
1636
|
+
children: [alias.networkId && /* @__PURE__ */ jsx(NetworkStatusBadge, {
|
|
1637
|
+
network: network ?? null,
|
|
1638
|
+
className: "shrink-0 gap-1.5 px-2 py-1"
|
|
1639
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1640
|
+
className: "min-w-0 flex-1",
|
|
1641
|
+
children: /* @__PURE__ */ jsx(AddressLabelProvider, {
|
|
1642
|
+
resolveLabel: () => void 0,
|
|
1643
|
+
children: /* @__PURE__ */ jsx(AddressDisplay, {
|
|
1644
|
+
address: alias.address,
|
|
1645
|
+
truncate: false,
|
|
1646
|
+
showCopyButton: true,
|
|
1647
|
+
explorerUrl,
|
|
1648
|
+
className: "text-xs text-muted-foreground"
|
|
1649
|
+
})
|
|
1650
|
+
})
|
|
1651
|
+
})]
|
|
1652
|
+
})]
|
|
1653
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1654
|
+
className: "flex shrink-0 gap-1",
|
|
1655
|
+
children: editing ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
1656
|
+
variant: "ghost",
|
|
1657
|
+
size: "icon",
|
|
1658
|
+
className: "h-7 w-7",
|
|
1659
|
+
onClick: handleSave,
|
|
1660
|
+
disabled: busy,
|
|
1661
|
+
"aria-label": "Save",
|
|
1662
|
+
children: /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" })
|
|
1663
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1664
|
+
variant: "ghost",
|
|
1665
|
+
size: "icon",
|
|
1666
|
+
className: "h-7 w-7",
|
|
1667
|
+
onClick: handleCancel,
|
|
1668
|
+
disabled: busy,
|
|
1669
|
+
"aria-label": "Cancel",
|
|
1670
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
1671
|
+
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
1672
|
+
variant: "ghost",
|
|
1673
|
+
size: "icon",
|
|
1674
|
+
className: "h-7 w-7",
|
|
1675
|
+
onClick: handleEdit,
|
|
1676
|
+
disabled: busy,
|
|
1677
|
+
"aria-label": "Edit alias",
|
|
1678
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" })
|
|
1679
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1680
|
+
variant: "ghost",
|
|
1681
|
+
size: "icon",
|
|
1682
|
+
className: "h-7 w-7 text-destructive hover:text-destructive",
|
|
1683
|
+
onClick: handleRemove,
|
|
1684
|
+
disabled: busy,
|
|
1685
|
+
"aria-label": "Remove alias",
|
|
1686
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
1687
|
+
})] })
|
|
1688
|
+
})]
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
//#endregion
|
|
1693
|
+
//#region src/components/AddressBookWidget/ImportExportBar.tsx
|
|
1694
|
+
/** Bar for importing and exporting address book aliases. */
|
|
1695
|
+
function ImportExportBar({ onExport, onImport, exportDisabled }) {
|
|
1696
|
+
const fileInputRef = useRef(null);
|
|
1697
|
+
const handleImportClick = useCallback(() => {
|
|
1698
|
+
fileInputRef.current?.click();
|
|
1699
|
+
}, []);
|
|
1700
|
+
const handleFileChange = useCallback(async (e) => {
|
|
1701
|
+
const file = e.target.files?.[0];
|
|
1702
|
+
if (!file) return;
|
|
1703
|
+
await onImport(file);
|
|
1704
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
1705
|
+
}, [onImport]);
|
|
1706
|
+
const handleExport = useCallback(async () => {
|
|
1707
|
+
await onExport();
|
|
1708
|
+
}, [onExport]);
|
|
1709
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(OverflowMenu, { items: useMemo(() => [{
|
|
1710
|
+
id: "export",
|
|
1711
|
+
label: "Export",
|
|
1712
|
+
icon: /* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }),
|
|
1713
|
+
disabled: exportDisabled,
|
|
1714
|
+
onSelect: handleExport
|
|
1715
|
+
}, {
|
|
1716
|
+
id: "import",
|
|
1717
|
+
label: "Import",
|
|
1718
|
+
icon: /* @__PURE__ */ jsx(Upload, { className: "mr-2 h-4 w-4" }),
|
|
1719
|
+
onSelect: handleImportClick
|
|
1720
|
+
}], [
|
|
1721
|
+
exportDisabled,
|
|
1722
|
+
handleExport,
|
|
1723
|
+
handleImportClick
|
|
1724
|
+
]) }), /* @__PURE__ */ jsx("input", {
|
|
1725
|
+
ref: fileInputRef,
|
|
1726
|
+
type: "file",
|
|
1727
|
+
accept: ".json",
|
|
1728
|
+
className: "hidden",
|
|
1729
|
+
onChange: handleFileChange
|
|
1730
|
+
})] });
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
//#endregion
|
|
1734
|
+
//#region src/components/AddressBookWidget/AddressBookWidget.tsx
|
|
1735
|
+
/** Widget for managing a personal address book with aliases, search, and network filtering. */
|
|
1736
|
+
function AddressBookWidget({ aliases, isLoading, onSave, onRemove, onClear, onExport, onImport, currentNetworkId, resolveNetwork, resolveExplorerUrl, adapter, resolveAdapter, addressPlaceholder, resolveAddressPlaceholder, networks, filterNetworkIds, onFilterNetworkIdsChange, className }) {
|
|
1737
|
+
const [search, setSearch] = useState("");
|
|
1738
|
+
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
|
1739
|
+
const [confirmClear, setConfirmClear] = useState(false);
|
|
1740
|
+
const [clearInput, setClearInput] = useState("");
|
|
1741
|
+
const activeFilterIds = useMemo(() => filterNetworkIds ?? [], [filterNetworkIds]);
|
|
1742
|
+
const hasActiveFilter = activeFilterIds.length > 0;
|
|
1743
|
+
const filteredAliases = useMemo(() => {
|
|
1744
|
+
if (!aliases) return void 0;
|
|
1745
|
+
if (!search.trim()) return aliases;
|
|
1746
|
+
const lower = search.toLowerCase();
|
|
1747
|
+
return aliases.filter((a) => a.alias.toLowerCase().includes(lower) || a.address.toLowerCase().includes(lower));
|
|
1748
|
+
}, [aliases, search]);
|
|
1749
|
+
const handleClear = useCallback(async () => {
|
|
1750
|
+
await onClear();
|
|
1751
|
+
setConfirmClear(false);
|
|
1752
|
+
setClearInput("");
|
|
1753
|
+
}, [onClear]);
|
|
1754
|
+
const handleCancelClear = useCallback(() => {
|
|
1755
|
+
setConfirmClear(false);
|
|
1756
|
+
setClearInput("");
|
|
1757
|
+
}, []);
|
|
1758
|
+
const canFilter = networks && networks.length > 0 && onFilterNetworkIdsChange;
|
|
1759
|
+
if (isLoading || aliases === void 0) return /* @__PURE__ */ jsxs(Card, {
|
|
1760
|
+
className: cn("w-full", className),
|
|
1761
|
+
children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
|
|
1762
|
+
className: "flex items-center gap-2 text-lg",
|
|
1763
|
+
children: [/* @__PURE__ */ jsx(BookUser, { className: "h-5 w-5" }), "Address Book"]
|
|
1764
|
+
}) }), /* @__PURE__ */ jsx(CardContent, {
|
|
1765
|
+
className: "flex items-center justify-center py-12",
|
|
1766
|
+
children: /* @__PURE__ */ jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" })
|
|
1767
|
+
})]
|
|
1768
|
+
});
|
|
1769
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
1770
|
+
className: cn("w-full", className),
|
|
1771
|
+
children: [
|
|
1772
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", {
|
|
1773
|
+
className: "flex items-center justify-between",
|
|
1774
|
+
children: [/* @__PURE__ */ jsxs(CardTitle, {
|
|
1775
|
+
className: "flex items-center gap-2 text-lg",
|
|
1776
|
+
children: [
|
|
1777
|
+
/* @__PURE__ */ jsx(BookUser, { className: "h-5 w-5" }),
|
|
1778
|
+
"Address Book",
|
|
1779
|
+
aliases.length > 0 && /* @__PURE__ */ jsxs("span", {
|
|
1780
|
+
className: "text-sm font-normal text-muted-foreground",
|
|
1781
|
+
children: [
|
|
1782
|
+
"(",
|
|
1783
|
+
aliases.length,
|
|
1784
|
+
")"
|
|
1785
|
+
]
|
|
1786
|
+
})
|
|
1787
|
+
]
|
|
1788
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1789
|
+
className: "flex items-center gap-2",
|
|
1790
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
1791
|
+
size: "sm",
|
|
1792
|
+
onClick: () => setAddDialogOpen(true),
|
|
1793
|
+
children: [/* @__PURE__ */ jsx(Plus, {
|
|
1794
|
+
className: "mr-1.5 h-3.5 w-3.5",
|
|
1795
|
+
"aria-hidden": "true"
|
|
1796
|
+
}), "Add Alias"]
|
|
1797
|
+
}), /* @__PURE__ */ jsx(ImportExportBar, {
|
|
1798
|
+
onExport,
|
|
1799
|
+
onImport,
|
|
1800
|
+
exportDisabled: aliases.length === 0
|
|
1801
|
+
})]
|
|
1802
|
+
})]
|
|
1803
|
+
}) }),
|
|
1804
|
+
/* @__PURE__ */ jsx(AddAliasDialog, {
|
|
1805
|
+
open: addDialogOpen,
|
|
1806
|
+
onOpenChange: setAddDialogOpen,
|
|
1807
|
+
onSave,
|
|
1808
|
+
currentNetworkId,
|
|
1809
|
+
adapter,
|
|
1810
|
+
resolveAdapter,
|
|
1811
|
+
addressPlaceholder,
|
|
1812
|
+
resolveAddressPlaceholder,
|
|
1813
|
+
resolveNetwork,
|
|
1814
|
+
networks
|
|
1815
|
+
}),
|
|
1816
|
+
/* @__PURE__ */ jsxs(CardContent, {
|
|
1817
|
+
className: "space-y-4",
|
|
1818
|
+
children: [(aliases.length > 0 || hasActiveFilter) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1819
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1820
|
+
className: "flex items-center gap-2",
|
|
1821
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1822
|
+
className: "relative flex-1",
|
|
1823
|
+
children: [/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ jsx(Input, {
|
|
1824
|
+
placeholder: "Search by alias or address…",
|
|
1825
|
+
value: search,
|
|
1826
|
+
onChange: (e) => setSearch(e.target.value),
|
|
1827
|
+
className: "pl-8"
|
|
1828
|
+
})]
|
|
1829
|
+
}), canFilter && /* @__PURE__ */ jsx(NetworkSelector, {
|
|
1830
|
+
multiple: true,
|
|
1831
|
+
networks,
|
|
1832
|
+
selectedNetworkIds: activeFilterIds,
|
|
1833
|
+
onSelectionChange: onFilterNetworkIdsChange,
|
|
1834
|
+
getNetworkLabel: (n) => n.name,
|
|
1835
|
+
getNetworkId: (n) => n.id,
|
|
1836
|
+
getNetworkIcon: (n) => /* @__PURE__ */ jsx(NetworkIcon, {
|
|
1837
|
+
network: n,
|
|
1838
|
+
size: 14
|
|
1839
|
+
}),
|
|
1840
|
+
getNetworkType: (n) => n.type,
|
|
1841
|
+
groupByEcosystem: true,
|
|
1842
|
+
getEcosystem: (n) => n.ecosystem.toUpperCase(),
|
|
1843
|
+
renderTrigger: ({ selectedCount }) => /* @__PURE__ */ jsxs(Button, {
|
|
1844
|
+
variant: "outline",
|
|
1845
|
+
size: "icon",
|
|
1846
|
+
className: "relative shrink-0",
|
|
1847
|
+
children: [/* @__PURE__ */ jsx(Filter, { className: "h-4 w-4" }), selectedCount > 0 && /* @__PURE__ */ jsx("span", {
|
|
1848
|
+
className: "absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground",
|
|
1849
|
+
children: selectedCount
|
|
1850
|
+
})]
|
|
1851
|
+
})
|
|
1852
|
+
})]
|
|
1853
|
+
}),
|
|
1854
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1855
|
+
className: "space-y-2",
|
|
1856
|
+
children: [filteredAliases?.map((alias) => /* @__PURE__ */ jsx(AliasRow, {
|
|
1857
|
+
alias,
|
|
1858
|
+
onSave,
|
|
1859
|
+
onRemove,
|
|
1860
|
+
resolveNetwork,
|
|
1861
|
+
resolveExplorerUrl
|
|
1862
|
+
}, alias.id)), filteredAliases?.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
1863
|
+
className: "py-4 text-center text-sm text-muted-foreground",
|
|
1864
|
+
children: hasActiveFilter ? "No aliases match the current filters." : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1865
|
+
"No aliases match “",
|
|
1866
|
+
search,
|
|
1867
|
+
"”"
|
|
1868
|
+
] })
|
|
1869
|
+
})]
|
|
1870
|
+
}),
|
|
1871
|
+
/* @__PURE__ */ jsx("div", {
|
|
1872
|
+
className: "border-t pt-4",
|
|
1873
|
+
children: confirmClear ? /* @__PURE__ */ jsxs("div", {
|
|
1874
|
+
className: "space-y-2",
|
|
1875
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
1876
|
+
className: "text-sm text-muted-foreground",
|
|
1877
|
+
children: [
|
|
1878
|
+
"Type ",
|
|
1879
|
+
/* @__PURE__ */ jsx("span", {
|
|
1880
|
+
className: "font-mono font-semibold",
|
|
1881
|
+
children: "clear"
|
|
1882
|
+
}),
|
|
1883
|
+
" to confirm removing all aliases."
|
|
1884
|
+
]
|
|
1885
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1886
|
+
className: "flex gap-2",
|
|
1887
|
+
children: [
|
|
1888
|
+
/* @__PURE__ */ jsx(Input, {
|
|
1889
|
+
value: clearInput,
|
|
1890
|
+
onChange: (e) => setClearInput(e.target.value),
|
|
1891
|
+
placeholder: "Type \"clear\"",
|
|
1892
|
+
className: "max-w-[200px]",
|
|
1893
|
+
autoFocus: true
|
|
1894
|
+
}),
|
|
1895
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1896
|
+
variant: "destructive",
|
|
1897
|
+
size: "sm",
|
|
1898
|
+
disabled: clearInput !== "clear",
|
|
1899
|
+
onClick: handleClear,
|
|
1900
|
+
children: "Confirm"
|
|
1901
|
+
}),
|
|
1902
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1903
|
+
variant: "outline",
|
|
1904
|
+
size: "sm",
|
|
1905
|
+
onClick: handleCancelClear,
|
|
1906
|
+
children: "Cancel"
|
|
1907
|
+
})
|
|
1908
|
+
]
|
|
1909
|
+
})]
|
|
1910
|
+
}) : /* @__PURE__ */ jsxs(Button, {
|
|
1911
|
+
variant: "outline",
|
|
1912
|
+
size: "sm",
|
|
1913
|
+
className: "text-destructive hover:text-destructive",
|
|
1914
|
+
onClick: () => setConfirmClear(true),
|
|
1915
|
+
children: [/* @__PURE__ */ jsx(Trash2, {
|
|
1916
|
+
className: "mr-1.5 h-3.5 w-3.5",
|
|
1917
|
+
"aria-hidden": "true"
|
|
1918
|
+
}), "Clear All"]
|
|
1919
|
+
})
|
|
1920
|
+
})
|
|
1921
|
+
] }), aliases.length === 0 && !hasActiveFilter && /* @__PURE__ */ jsx(EmptyState, {
|
|
1922
|
+
icon: /* @__PURE__ */ jsx(BookUser, { className: "h-10 w-10" }),
|
|
1923
|
+
title: "No aliases yet",
|
|
1924
|
+
description: "Add your first alias above to start building your address book.",
|
|
1925
|
+
size: "small"
|
|
1926
|
+
})]
|
|
1927
|
+
})
|
|
1928
|
+
]
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
//#endregion
|
|
1933
|
+
//#region src/components/AddressBookWidget/AliasEditPopover.tsx
|
|
1934
|
+
/**
|
|
1935
|
+
* AliasEditPopover
|
|
1936
|
+
*
|
|
1937
|
+
* Floating popover anchored to the pencil-icon click position.
|
|
1938
|
+
* Allows creating, editing, and removing an alias for a single address.
|
|
1939
|
+
*
|
|
1940
|
+
* This is a presentational component — all storage operations are
|
|
1941
|
+
* provided via callback props so it remains storage-agnostic.
|
|
1942
|
+
*/
|
|
1943
|
+
/** Inline popover for creating, editing, or removing an address alias. */
|
|
1944
|
+
function AliasEditPopover({ address, networkId, anchorRect, onClose, onLookup, onSave, onRemove }) {
|
|
1945
|
+
const [alias, setAlias] = useState("");
|
|
1946
|
+
const [existingId, setExistingId] = useState(null);
|
|
1947
|
+
const [busy, setBusy] = useState(false);
|
|
1948
|
+
const [loaded, setLoaded] = useState(false);
|
|
1949
|
+
const inputRef = useRef(null);
|
|
1950
|
+
useEffect(() => {
|
|
1951
|
+
let cancelled = false;
|
|
1952
|
+
onLookup(address, networkId).then((record) => {
|
|
1953
|
+
if (cancelled) return;
|
|
1954
|
+
if (record) {
|
|
1955
|
+
setAlias(record.alias);
|
|
1956
|
+
setExistingId(record.id);
|
|
1957
|
+
}
|
|
1958
|
+
setLoaded(true);
|
|
1959
|
+
}).catch(() => {
|
|
1960
|
+
if (!cancelled) setLoaded(true);
|
|
1961
|
+
});
|
|
1962
|
+
return () => {
|
|
1963
|
+
cancelled = true;
|
|
1964
|
+
};
|
|
1965
|
+
}, [
|
|
1966
|
+
address,
|
|
1967
|
+
networkId,
|
|
1968
|
+
onLookup
|
|
1969
|
+
]);
|
|
1970
|
+
useEffect(() => {
|
|
1971
|
+
if (loaded) inputRef.current?.focus();
|
|
1972
|
+
}, [loaded]);
|
|
1973
|
+
const handleSave = useCallback(async () => {
|
|
1974
|
+
const trimmed = alias.trim();
|
|
1975
|
+
if (!trimmed) return;
|
|
1976
|
+
setBusy(true);
|
|
1977
|
+
try {
|
|
1978
|
+
await onSave({
|
|
1979
|
+
address,
|
|
1980
|
+
alias: trimmed,
|
|
1981
|
+
networkId
|
|
1982
|
+
});
|
|
1983
|
+
onClose();
|
|
1984
|
+
} finally {
|
|
1985
|
+
setBusy(false);
|
|
1986
|
+
}
|
|
1987
|
+
}, [
|
|
1988
|
+
address,
|
|
1989
|
+
alias,
|
|
1990
|
+
networkId,
|
|
1991
|
+
onSave,
|
|
1992
|
+
onClose
|
|
1993
|
+
]);
|
|
1994
|
+
const handleRemove = useCallback(async () => {
|
|
1995
|
+
if (!existingId) return;
|
|
1996
|
+
setBusy(true);
|
|
1997
|
+
try {
|
|
1998
|
+
await onRemove(existingId);
|
|
1999
|
+
onClose();
|
|
2000
|
+
} finally {
|
|
2001
|
+
setBusy(false);
|
|
2002
|
+
}
|
|
2003
|
+
}, [
|
|
2004
|
+
existingId,
|
|
2005
|
+
onRemove,
|
|
2006
|
+
onClose
|
|
2007
|
+
]);
|
|
2008
|
+
const handleKeyDown = useCallback((e) => {
|
|
2009
|
+
if (e.key === "Enter") handleSave();
|
|
2010
|
+
if (e.key === "Escape") onClose();
|
|
2011
|
+
}, [handleSave, onClose]);
|
|
2012
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
2013
|
+
open: true,
|
|
2014
|
+
onOpenChange: (open) => !open && onClose(),
|
|
2015
|
+
children: [/* @__PURE__ */ jsx(PopoverAnchor, { style: {
|
|
2016
|
+
position: "fixed",
|
|
2017
|
+
left: anchorRect.x,
|
|
2018
|
+
top: anchorRect.y,
|
|
2019
|
+
width: 0,
|
|
2020
|
+
height: 0,
|
|
2021
|
+
pointerEvents: "none"
|
|
2022
|
+
} }), /* @__PURE__ */ jsx(PopoverContent, {
|
|
2023
|
+
side: "bottom",
|
|
2024
|
+
align: "start",
|
|
2025
|
+
className: "w-64 space-y-3 p-3",
|
|
2026
|
+
children: !loaded ? /* @__PURE__ */ jsx("p", {
|
|
2027
|
+
className: "text-sm text-muted-foreground",
|
|
2028
|
+
children: "Loading…"
|
|
2029
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
2030
|
+
htmlFor: "alias-edit-input",
|
|
2031
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
2032
|
+
children: "Alias"
|
|
2033
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
2034
|
+
ref: inputRef,
|
|
2035
|
+
id: "alias-edit-input",
|
|
2036
|
+
type: "text",
|
|
2037
|
+
value: alias,
|
|
2038
|
+
onChange: (e) => setAlias(e.target.value),
|
|
2039
|
+
onKeyDown: handleKeyDown,
|
|
2040
|
+
disabled: busy,
|
|
2041
|
+
placeholder: "e.g. Treasury",
|
|
2042
|
+
className: "mt-1 w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
2043
|
+
})] }), /* @__PURE__ */ jsxs("div", {
|
|
2044
|
+
className: "flex justify-between gap-2",
|
|
2045
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2046
|
+
className: "flex gap-2",
|
|
2047
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
2048
|
+
type: "button",
|
|
2049
|
+
onClick: handleSave,
|
|
2050
|
+
disabled: busy || !alias.trim(),
|
|
2051
|
+
className: "rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground shadow-sm hover:bg-primary/90 disabled:opacity-50",
|
|
2052
|
+
children: "Save"
|
|
2053
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
2054
|
+
type: "button",
|
|
2055
|
+
onClick: onClose,
|
|
2056
|
+
disabled: busy,
|
|
2057
|
+
className: "rounded-md border px-3 py-1 text-xs font-medium hover:bg-accent disabled:opacity-50",
|
|
2058
|
+
children: "Cancel"
|
|
2059
|
+
})]
|
|
2060
|
+
}), existingId && /* @__PURE__ */ jsx("button", {
|
|
2061
|
+
type: "button",
|
|
2062
|
+
onClick: handleRemove,
|
|
2063
|
+
disabled: busy,
|
|
2064
|
+
className: "rounded-md px-3 py-1 text-xs font-medium text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
2065
|
+
children: "Remove"
|
|
2066
|
+
})]
|
|
2067
|
+
})] })
|
|
2068
|
+
})]
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
//#endregion
|
|
2073
|
+
//#region src/components/AddressBookWidget/useAliasEditState.ts
|
|
2074
|
+
/**
|
|
2075
|
+
* useAliasEditState
|
|
2076
|
+
*
|
|
2077
|
+
* Pure UI state hook for managing the inline alias edit popover.
|
|
2078
|
+
* Tracks which address is being edited and the click anchor position
|
|
2079
|
+
* so the popover can be positioned near the pencil icon.
|
|
2080
|
+
*
|
|
2081
|
+
* Returns an `onEditLabel` callback compatible with `AddressLabelProvider`.
|
|
2082
|
+
*/
|
|
2083
|
+
/** Manages the edit state for the inline alias popover. */
|
|
2084
|
+
function useAliasEditState(defaultNetworkId) {
|
|
2085
|
+
const [editing, setEditing] = useState(null);
|
|
2086
|
+
const lastClickRef = useRef({
|
|
2087
|
+
x: 0,
|
|
2088
|
+
y: 0
|
|
2089
|
+
});
|
|
2090
|
+
return {
|
|
2091
|
+
editing,
|
|
2092
|
+
onEditLabel: useCallback((address, networkId) => {
|
|
2093
|
+
const { x, y } = lastClickRef.current;
|
|
2094
|
+
const anchorRect = new DOMRect(x, y, 0, 0);
|
|
2095
|
+
setEditing({
|
|
2096
|
+
address,
|
|
2097
|
+
networkId: networkId ?? defaultNetworkId,
|
|
2098
|
+
anchorRect
|
|
2099
|
+
});
|
|
2100
|
+
}, [defaultNetworkId]),
|
|
2101
|
+
handleClose: useCallback(() => {
|
|
2102
|
+
setEditing(null);
|
|
2103
|
+
}, []),
|
|
2104
|
+
lastClickRef
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
|
|
1417
2108
|
//#endregion
|
|
1418
2109
|
//#region src/components/ContractStateWidget/components/FunctionResult.tsx
|
|
1419
2110
|
/**
|
|
@@ -2005,7 +2696,7 @@ function NetworkServiceSettingsPanel({ adapter, networkId, service, onSettingsCh
|
|
|
2005
2696
|
//#endregion
|
|
2006
2697
|
//#region src/components/network/NetworkSettingsDialog.tsx
|
|
2007
2698
|
const NetworkSettingsDialog = ({ isOpen, onOpenChange, networkConfig, adapter }) => {
|
|
2008
|
-
const services = adapter?.getNetworkServiceForms?.() ?? [];
|
|
2699
|
+
const services = filterEnabledServiceForms(adapter?.getNetworkServiceForms?.() ?? []);
|
|
2009
2700
|
return /* @__PURE__ */ jsx(Dialog, {
|
|
2010
2701
|
open: isOpen,
|
|
2011
2702
|
onOpenChange,
|
|
@@ -2089,5 +2780,5 @@ const WalletConnectionWithSettings = () => {
|
|
|
2089
2780
|
};
|
|
2090
2781
|
|
|
2091
2782
|
//#endregion
|
|
2092
|
-
export { ContractActionBar, ContractStateWidget, DynamicFormField, ExecutionConfigDisplay, NetworkSettingsDialog, TransactionExecuteButton, TransactionForm, WalletConnectionWithSettings, createAddressTransform, createArrayObjectTransform, createArrayTransform, createBigIntTransform, createBooleanTransform, createComplexTypeTransform, createDefaultFormValues, createNumberTransform, createObjectTransform, createTextTransform, createTransformForFieldType, generateDefaultValue, getDefaultValueByFieldType, rendererConfig, validateField };
|
|
2783
|
+
export { AddressBookWidget, AliasEditPopover, ContractActionBar, ContractStateWidget, DynamicFormField, ExecutionConfigDisplay, NetworkSettingsDialog, TransactionExecuteButton, TransactionForm, WalletConnectionWithSettings, createAddressTransform, createArrayObjectTransform, createArrayTransform, createBigIntTransform, createBooleanTransform, createComplexTypeTransform, createDefaultFormValues, createNumberTransform, createObjectTransform, createTextTransform, createTransformForFieldType, generateDefaultValue, getDefaultValueByFieldType, rendererConfig, useAliasEditState, validateField };
|
|
2093
2784
|
//# sourceMappingURL=index.mjs.map
|