feat: add json view

This commit is contained in:
Victor Oliva
2024-10-23 15:57:51 +02:00
parent f8592ee564
commit 86e376b39f
10 changed files with 578 additions and 143 deletions
+8 -1
View File
@@ -25,6 +25,7 @@
"@polkadot-api/utils": "^0.1.2",
"@polkadot-api/ws-provider": "^0.3.2",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
"@react-rxjs/core": "^0.10.7",
"@react-rxjs/utils": "^0.9.7",
"lucide-react": "^0.453.0",
@@ -33,6 +34,7 @@
"react-portal": "^4.2.2",
"react-router-dom": "^6.27.0",
"react-svg": "^16.1.34",
"react18-json-view": "^0.2.8",
"rxjs": "^7.8.1",
"save-as": "^0.1.8",
"tailwind-merge": "^2.5.4"
@@ -56,5 +58,10 @@
"typescript-eslint": "^8.7.0",
"vite": "^5.4.8"
},
"packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228"
"packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228",
"pnpm": {
"patchedDependencies": {
"react18-json-view@0.2.8": "patches/react18-json-view@0.2.8.patch"
}
}
}
+81
View File
@@ -0,0 +1,81 @@
diff --git a/dist/components/json-view.d.ts b/dist/components/json-view.d.ts
index 27dfc6d406b7862061520ab99114f10b61e4c773..275b18c6c755ea630549b0e61bf75cc8a3daa63b 100644
--- a/dist/components/json-view.d.ts
+++ b/dist/components/json-view.d.ts
@@ -51,6 +51,7 @@ export declare const JsonViewContext: import("react").Context<{
ignoreLargeArray: boolean;
}>;
export interface JsonViewProps {
+ replacer?: (node: any) => any;
src: any;
collapseStringsAfterLength?: number;
collapseStringMode?: 'directly' | 'word' | 'address';
diff --git a/dist/es/index.mjs b/dist/es/index.mjs
index 663884480f7252dac251b9343d80f1e85ea93151..c4e6458358a9f58ce99d29d46850aceeb33a1d20 100644
--- a/dist/es/index.mjs
+++ b/dist/es/index.mjs
@@ -295,7 +295,7 @@ function LargeArrayNode({ originNode, node, depth, index, deleteHandle: _deleteS
forceUpdate();
};
const Icons = (jsxs(Fragment, { children: [!fold && (jsxs("span", Object.assign({ onClick: () => setFold(true), className: 'jv-size-chevron' }, { children: [ifDisplay(displaySize, depth, fold) && jsxs("span", Object.assign({ className: 'jv-size' }, { children: [objectSize(node), " Items"] })), jsx(SvgAngleDown, { className: 'jv-chevron' })] }))), !fold && enableClipboard && customCopy(customOptions) && jsx(CopyButton, { node: node })] }));
- return (jsxs("div", { children: [jsx("span", { children: '[' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: node.map((n, i) => (jsx(NameValue, { indexOrName: i + startIndex, value: n, depth: depth, parent: node, deleteHandle: deleteHandle, editHandle: editHandle }, String(index) + String(i)))) }))) : (jsxs("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: [startIndex, " ... ", startIndex + node.length - 1] }))), jsx("span", { children: ']' })] }));
+ return (jsxs("div", { children: [jsx("span", { children: '[' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: node.map((n, i) => (jsx(NameValue, { indexOrName: i + startIndex, value: n, depth: depth, parent: node, deleteHandle: deleteHandle, editHandle: editHandle }, String(index) + String(i)))) }))) : (jsxs("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: [startIndex, " … ", startIndex + node.length - 1] }))), jsx("span", { children: ']' })] }));
}
function LargeArray({ node, depth, deleteHandle: _deleteSelf, indexOrName, customOptions }) {
@@ -344,7 +344,7 @@ function LargeArray({ node, depth, deleteHandle: _deleteSelf, indexOrName, custo
const Icons = (jsxs(Fragment, { children: [!fold && !isEditing && (jsxs("span", Object.assign({ onClick: () => setFold(true), className: 'jv-size-chevron' }, { children: [ifDisplay(displaySize, depth, fold) && jsxs("span", Object.assign({ className: 'jv-size' }, { children: [node.length, " Items"] })), jsx(SvgAngleDown, { className: 'jv-chevron' })] }))), isEditing && jsx(SvgDone, { className: 'json-view--edit', style: { display: 'inline-block' }, onClick: adding ? add : deleteSelf }), isEditing && jsx(SvgCancel, { className: 'json-view--edit', style: { display: 'inline-block' }, onClick: cancel }), !fold && !isEditing && enableClipboard && customCopy(customOptions) && jsx(CopyButton, { node: node }), !fold && !isEditing && editableAdd(editable) && customAdd(customOptions) && (jsx(SvgAddSquare, { className: 'json-view--edit', onClick: () => {
add();
} })), !fold && !isEditing && editableDelete(editable) && customDelete(customOptions) && _deleteSelf && (jsx(SvgTrash, { className: 'json-view--edit', onClick: () => setDeleting(true) }))] }));
- return (jsxs(Fragment, { children: [jsx("span", { children: '[' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: nestCollapsedArray.map((item, index) => (jsx(LargeArrayNode, { originNode: node, node: item, depth: depth, index: index, startIndex: index * 100 }, String(indexOrName) + String(index)))) }))) : (jsx("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: "..." }))), jsx("span", { children: ']' }), fold && ifDisplay(displaySize, depth, fold) && (jsxs("span", Object.assign({ onClick: () => setFold(false), className: 'jv-size' }, { children: [node.length, " Items"] })))] }));
+ return (jsxs(Fragment, { children: [jsx("span", { children: '[' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: nestCollapsedArray.map((item, index) => (jsx(LargeArrayNode, { originNode: node, node: item, depth: depth, index: index, startIndex: index * 100 }, String(indexOrName) + String(index)))) }))) : (jsx("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: "…" }))), jsx("span", { children: ']' }), fold && ifDisplay(displaySize, depth, fold) && (jsxs("span", Object.assign({ onClick: () => setFold(false), className: 'jv-size' }, { children: [node.length, " Items"] })))] }));
}
function ObjectNode({ node, depth, indexOrName, deleteHandle: _deleteSelf, customOptions }) {
@@ -457,10 +457,10 @@ function ObjectNode({ node, depth, indexOrName, deleteHandle: _deleteSelf, custo
}
} })), !fold && !isEditing && editableDelete(editable) && customDelete(customOptions) && _deleteSelf && (jsx(SvgTrash, { className: 'json-view--edit', onClick: () => setDeleting(true) }))] }));
if (Array.isArray(node)) {
- return (jsxs(Fragment, { children: [jsx("span", { children: '[' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: node.map((n, i) => (jsx(NameValue, { indexOrName: i, value: n, depth: depth, parent: node, deleteHandle: deleteHandle, editHandle: editHandle }, String(indexOrName) + String(i)))) }))) : (jsx("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: "..." }))), jsx("span", { children: ']' }), fold && ifDisplay(displaySize, depth, fold) && (jsxs("span", Object.assign({ onClick: () => setFold(false), className: 'jv-size' }, { children: [objectSize(node), " Items"] })))] }));
+ return (jsxs(Fragment, { children: [jsx("span", { children: '[' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: node.map((n, i) => (jsx(NameValue, { indexOrName: i, value: n, depth: depth, parent: node, deleteHandle: deleteHandle, editHandle: editHandle }, String(indexOrName) + String(i)))) }))) : (jsx("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: "…" }))), jsx("span", { children: ']' }), fold && ifDisplay(displaySize, depth, fold) && (jsxs("span", Object.assign({ onClick: () => setFold(false), className: 'jv-size' }, { children: [objectSize(node), " Items"] })))] }));
}
else if (isPlainObject) {
- return (jsxs(Fragment, { children: [jsx("span", { children: '{' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: Object.entries(node).map(([name, value]) => (jsx(NameValue, { indexOrName: name, value: value, depth: depth, parent: node, deleteHandle: deleteHandle, editHandle: editHandle }, String(indexOrName) + String(name)))) }))) : (jsx("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: "..." }))), jsx("span", { children: '}' }), fold && ifDisplay(displaySize, depth, fold) && (jsxs("span", Object.assign({ onClick: () => setFold(false), className: 'jv-size' }, { children: [objectSize(node), " Items"] })))] }));
+ return (jsxs(Fragment, { children: [jsx("span", { children: '{' }), Icons, !fold ? (jsx("div", Object.assign({ className: 'jv-indent' }, { children: Object.entries(node).map(([name, value]) => (jsx(NameValue, { indexOrName: name, value: value, depth: depth, parent: node, deleteHandle: deleteHandle, editHandle: editHandle }, String(indexOrName) + String(name)))) }))) : (jsx("button", Object.assign({ onClick: () => setFold(false), className: 'jv-button' }, { children: "…" }))), jsx("span", { children: '}' }), fold && ifDisplay(displaySize, depth, fold) && (jsxs("span", Object.assign({ onClick: () => setFold(false), className: 'jv-size' }, { children: [objectSize(node), " Items"] })))] }));
}
return null;
}
@@ -475,7 +475,7 @@ const LongString = React__default.forwardRef(({ str, className, ctrlClick }, ref
? customizeCollapseStringUI(str_show, truncated)
: typeof customizeCollapseStringUI === 'string'
? customizeCollapseStringUI
- : '...';
+ : '…';
const clickToTruncateOrEdit = (event) => {
var _a;
if ((event.ctrlKey || event.metaKey) && ctrlClick) {
@@ -556,7 +556,8 @@ var SvgLink = function SvgLink(props) {
};
function JsonNode({ node, depth, deleteHandle: _deleteHandle, indexOrName, parent, editHandle }) {
- const { collapseStringsAfterLength, enableClipboard, editable, src, onDelete, onChange, customizeNode, matchesURL, urlRegExp } = useContext(JsonViewContext);
+ const { replacer, collapseStringsAfterLength, enableClipboard, editable, src, onDelete, onChange, customizeNode, matchesURL, urlRegExp } = useContext(JsonViewContext);
+ node = replacer ? replacer(node) : node;
let customReturn;
if (typeof customizeNode === 'function')
customReturn = safeCall(customizeNode, [{ node, depth, indexOrName }]);
@@ -693,12 +694,13 @@ const JsonViewContext = createContext({
urlRegExp: defaultURLRegExp,
ignoreLargeArray: false
});
-function JsonView({ src: _src, collapseStringsAfterLength = 99, collapseStringMode = 'directly', customizeCollapseStringUI, collapseObjectsAfterLength = 99, collapsed, enableClipboard = true, editable = false, onEdit, onDelete, onAdd, onChange, dark = false, theme = 'default', customizeNode, customizeCopy = stringifyForCopying, displaySize, style, className, matchesURL = false, urlRegExp = defaultURLRegExp, ignoreLargeArray = false }) {
+function JsonView({ replacer, src: _src, collapseStringsAfterLength = 99, collapseStringMode = 'directly', customizeCollapseStringUI, collapseObjectsAfterLength = 99, collapsed, enableClipboard = true, editable = false, onEdit, onDelete, onAdd, onChange, dark = false, theme = 'default', customizeNode, customizeCopy = stringifyForCopying, displaySize, style, className, matchesURL = false, urlRegExp = defaultURLRegExp, ignoreLargeArray = false }) {
const [_, update] = useState(0);
const forceUpdate = useCallback(() => update(state => ++state), []);
const [src, setSrc] = useState(_src);
useEffect(() => setSrc(_src), [_src]);
return (jsx(JsonViewContext.Provider, Object.assign({ value: {
+ replacer,
src,
collapseStringsAfterLength,
collapseStringMode,
+242 -64
View File
@@ -4,13 +4,18 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
patchedDependencies:
react18-json-view@0.2.8:
hash: tfqbi5gr5whnc64n7zptszjgcy
path: patches/react18-json-view@0.2.8.patch
importers:
.:
dependencies:
'@headlessui/react':
specifier: ^2.1.10
version: 2.1.10(react-dom@18.3.1)(react@18.3.1)
version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@polkadot-api/known-chains':
specifier: ^0.5.4
version: 0.5.4
@@ -43,13 +48,16 @@ importers:
version: 0.3.2
'@radix-ui/react-toggle':
specifier: ^1.1.0
version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)
version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-toggle-group':
specifier: ^1.1.0
version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@react-rxjs/core':
specifier: ^0.10.7
version: 0.10.7(react@18.3.1)(rxjs@7.8.1)
'@react-rxjs/utils':
specifier: ^0.9.7
version: 0.9.7(@react-rxjs/core@0.10.7)(react@18.3.1)(rxjs@7.8.1)
version: 0.9.7(@react-rxjs/core@0.10.7(react@18.3.1)(rxjs@7.8.1))(react@18.3.1)(rxjs@7.8.1)
lucide-react:
specifier: ^0.453.0
version: 0.453.0(react@18.3.1)
@@ -61,13 +69,16 @@ importers:
version: 18.3.1(react@18.3.1)
react-portal:
specifier: ^4.2.2
version: 4.2.2(react-dom@18.3.1)(react@18.3.1)
version: 4.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-router-dom:
specifier: ^6.27.0
version: 6.27.0(react-dom@18.3.1)(react@18.3.1)
version: 6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-svg:
specifier: ^16.1.34
version: 16.1.34(react-dom@18.3.1)(react@18.3.1)
version: 16.1.34(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react18-json-view:
specifier: ^0.2.8
version: 0.2.8(patch_hash=tfqbi5gr5whnc64n7zptszjgcy)(react@18.3.1)
rxjs:
specifier: ^7.8.1
version: 7.8.1
@@ -95,19 +106,19 @@ importers:
version: 4.0.7
'@vitejs/plugin-react':
specifier: ^4.3.2
version: 4.3.3(vite@5.4.9)
version: 4.3.3(vite@5.4.9(@types/node@22.7.8))
autoprefixer:
specifier: ^10.4.20
version: 10.4.20(postcss@8.4.47)
eslint:
specifier: ^9.11.1
version: 9.13.0
version: 9.13.0(jiti@1.21.6)
eslint-plugin-react-hooks:
specifier: ^5.1.0-rc.0
version: 5.1.0-rc-fb9a90fa48-20240614(eslint@9.13.0)
version: 5.1.0-rc-fb9a90fa48-20240614(eslint@9.13.0(jiti@1.21.6))
eslint-plugin-react-refresh:
specifier: ^0.4.12
version: 0.4.13(eslint@9.13.0)
version: 0.4.13(eslint@9.13.0(jiti@1.21.6))
globals:
specifier: ^15.9.0
version: 15.11.0
@@ -125,7 +136,7 @@ importers:
version: 5.6.3
typescript-eslint:
specifier: ^8.7.0
version: 8.11.0(eslint@9.13.0)(typescript@5.6.3)
version: 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
vite:
specifier: ^5.4.8
version: 5.4.9(@types/node@22.7.8)
@@ -533,6 +544,19 @@ packages:
'@radix-ui/primitive@1.1.0':
resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==}
'@radix-ui/react-collection@1.1.0':
resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-compose-refs@1.1.0':
resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==}
peerDependencies:
@@ -542,6 +566,33 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-context@1.1.0':
resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-direction@1.1.0':
resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-id@1.1.0':
resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-primitive@2.0.0':
resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
peerDependencies:
@@ -555,6 +606,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-roving-focus@1.1.0':
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-slot@1.1.0':
resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
peerDependencies:
@@ -564,6 +628,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-toggle-group@1.1.0':
resolution: {integrity: sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-toggle@1.1.0':
resolution: {integrity: sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==}
peerDependencies:
@@ -595,6 +672,15 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-use-layout-effect@1.1.0':
resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@react-aria/focus@3.18.4':
resolution: {integrity: sha512-91J35077w9UNaMK1cpMUEFRkNNz0uZjnSwiyBCFuRdaVuivO53wNC9XtWSDNDdcO5cGy87vfJRVAiyoCn/mjqA==}
peerDependencies:
@@ -1509,6 +1595,11 @@ packages:
react: ^16.0.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
react18-json-view@0.2.8:
resolution: {integrity: sha512-uJlcf5PEDaba6yTqfcDAcMSYECZ15SLcpP94mLFTa/+fa1kZANjERqKzS7YxxsrGP4+jDxt6sIaglR0PbQcKPw==}
peerDependencies:
react: '>=16.8.0'
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
@@ -1977,9 +2068,9 @@ snapshots:
'@esbuild/win32-x64@0.21.5':
optional: true
'@eslint-community/eslint-utils@4.4.0(eslint@9.13.0)':
'@eslint-community/eslint-utils@4.4.0(eslint@9.13.0(jiti@1.21.6))':
dependencies:
eslint: 9.13.0
eslint: 9.13.0(jiti@1.21.6)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.11.1': {}
@@ -2025,15 +2116,15 @@ snapshots:
'@floating-ui/core': 1.6.8
'@floating-ui/utils': 0.2.8
'@floating-ui/react-dom@2.1.2(react-dom@18.3.1)(react@18.3.1)':
'@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/dom': 1.6.11
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@floating-ui/react@0.26.25(react-dom@18.3.1)(react@18.3.1)':
'@floating-ui/react@0.26.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react-dom': 2.1.2(react-dom@18.3.1)(react@18.3.1)
'@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@floating-ui/utils': 0.2.8
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@@ -2041,12 +2132,12 @@ snapshots:
'@floating-ui/utils@0.2.8': {}
'@headlessui/react@2.1.10(react-dom@18.3.1)(react@18.3.1)':
'@headlessui/react@2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react': 0.26.25(react-dom@18.3.1)(react@18.3.1)
'@floating-ui/react': 0.26.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@react-aria/focus': 3.18.4(react@18.3.1)
'@react-aria/interactions': 3.22.4(react@18.3.1)
'@tanstack/react-virtual': 3.10.8(react-dom@18.3.1)(react@18.3.1)
'@tanstack/react-virtual': 3.10.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@@ -2167,45 +2258,120 @@ snapshots:
'@radix-ui/primitive@1.1.0': {}
'@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
'@types/react': 18.3.11
react: 18.3.1
'@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)':
'@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-context': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
'@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-context@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-direction@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-id@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.11)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.0
'@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-context': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
'@radix-ui/react-slot@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@types/react': 18.3.11
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-toggle@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)':
'@radix-ui/react-toggle-group@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.0
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-context': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-toggle': 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
'@radix-ui/react-toggle@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.0
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.11
'@types/react-dom': 18.3.1
'@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
'@types/react': 18.3.11
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@18.3.1)
'@types/react': 18.3.11
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.11)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.11
'@react-aria/focus@3.18.4(react@18.3.1)':
dependencies:
@@ -2245,7 +2411,7 @@ snapshots:
rxjs: 7.8.1
use-sync-external-store: 1.2.2(react@18.3.1)
'@react-rxjs/utils@0.9.7(@react-rxjs/core@0.10.7)(react@18.3.1)(rxjs@7.8.1)':
'@react-rxjs/utils@0.9.7(@react-rxjs/core@0.10.7(react@18.3.1)(rxjs@7.8.1))(react@18.3.1)(rxjs@7.8.1)':
dependencies:
'@react-rxjs/core': 0.10.7(react@18.3.1)(rxjs@7.8.1)
react: 18.3.1
@@ -2326,7 +2492,7 @@ snapshots:
content-type: 1.0.5
tslib: 2.8.0
'@tanstack/react-virtual@3.10.8(react-dom@18.3.1)(react@18.3.1)':
'@tanstack/react-virtual@3.10.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/virtual-core': 3.10.8
react: 18.3.1
@@ -2378,31 +2544,33 @@ snapshots:
'@types/prop-types': 15.7.13
csstype: 3.1.3
'@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0)(eslint@9.13.0)(typescript@5.6.3)':
'@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)':
dependencies:
'@eslint-community/regexpp': 4.11.1
'@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/parser': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/scope-manager': 8.11.0
'@typescript-eslint/type-utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/type-utils': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/utils': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/visitor-keys': 8.11.0
eslint: 9.13.0
eslint: 9.13.0(jiti@1.21.6)
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
ts-api-utils: 1.3.0(typescript@5.6.3)
optionalDependencies:
typescript: 5.6.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
'@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.11.0
'@typescript-eslint/types': 8.11.0
'@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
'@typescript-eslint/visitor-keys': 8.11.0
debug: 4.3.7
eslint: 9.13.0
eslint: 9.13.0(jiti@1.21.6)
optionalDependencies:
typescript: 5.6.3
transitivePeerDependencies:
- supports-color
@@ -2412,12 +2580,13 @@ snapshots:
'@typescript-eslint/types': 8.11.0
'@typescript-eslint/visitor-keys': 8.11.0
'@typescript-eslint/type-utils@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
'@typescript-eslint/type-utils@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)':
dependencies:
'@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
'@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/utils': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
debug: 4.3.7
ts-api-utils: 1.3.0(typescript@5.6.3)
optionalDependencies:
typescript: 5.6.3
transitivePeerDependencies:
- eslint
@@ -2435,17 +2604,18 @@ snapshots:
minimatch: 9.0.5
semver: 7.6.3
ts-api-utils: 1.3.0(typescript@5.6.3)
optionalDependencies:
typescript: 5.6.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.11.0(eslint@9.13.0)(typescript@5.6.3)':
'@typescript-eslint/utils@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0)
'@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0(jiti@1.21.6))
'@typescript-eslint/scope-manager': 8.11.0
'@typescript-eslint/types': 8.11.0
'@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3)
eslint: 9.13.0
eslint: 9.13.0(jiti@1.21.6)
transitivePeerDependencies:
- supports-color
- typescript
@@ -2455,7 +2625,7 @@ snapshots:
'@typescript-eslint/types': 8.11.0
eslint-visitor-keys: 3.4.3
'@vitejs/plugin-react@4.3.3(vite@5.4.9)':
'@vitejs/plugin-react@4.3.3(vite@5.4.9(@types/node@22.7.8))':
dependencies:
'@babel/core': 7.25.8
'@babel/plugin-transform-react-jsx-self': 7.25.7(@babel/core@7.25.8)
@@ -2663,13 +2833,13 @@ snapshots:
escape-string-regexp@4.0.0: {}
eslint-plugin-react-hooks@5.1.0-rc-fb9a90fa48-20240614(eslint@9.13.0):
eslint-plugin-react-hooks@5.1.0-rc-fb9a90fa48-20240614(eslint@9.13.0(jiti@1.21.6)):
dependencies:
eslint: 9.13.0
eslint: 9.13.0(jiti@1.21.6)
eslint-plugin-react-refresh@0.4.13(eslint@9.13.0):
eslint-plugin-react-refresh@0.4.13(eslint@9.13.0(jiti@1.21.6)):
dependencies:
eslint: 9.13.0
eslint: 9.13.0(jiti@1.21.6)
eslint-scope@8.1.0:
dependencies:
@@ -2680,9 +2850,9 @@ snapshots:
eslint-visitor-keys@4.1.0: {}
eslint@9.13.0:
eslint@9.13.0(jiti@1.21.6):
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0)
'@eslint-community/eslint-utils': 4.4.0(eslint@9.13.0(jiti@1.21.6))
'@eslint-community/regexpp': 4.11.1
'@eslint/config-array': 0.18.0
'@eslint/core': 0.7.0
@@ -2717,6 +2887,8 @@ snapshots:
natural-compare: 1.4.0
optionator: 0.9.4
text-table: 0.2.0
optionalDependencies:
jiti: 1.21.6
transitivePeerDependencies:
- supports-color
@@ -3011,8 +3183,9 @@ snapshots:
postcss-load-config@4.0.2(postcss@8.4.47):
dependencies:
lilconfig: 3.1.2
postcss: 8.4.47
yaml: 2.6.0
optionalDependencies:
postcss: 8.4.47
postcss-nested@6.2.0(postcss@8.4.47):
dependencies:
@@ -3054,7 +3227,7 @@ snapshots:
react-is@16.13.1: {}
react-portal@4.2.2(react-dom@18.3.1)(react@18.3.1):
react-portal@4.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
prop-types: 15.8.1
react: 18.3.1
@@ -3062,7 +3235,7 @@ snapshots:
react-refresh@0.14.2: {}
react-router-dom@6.27.0(react-dom@18.3.1)(react@18.3.1):
react-router-dom@6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@remix-run/router': 1.20.0
react: 18.3.1
@@ -3074,7 +3247,7 @@ snapshots:
'@remix-run/router': 1.20.0
react: 18.3.1
react-svg@16.1.34(react-dom@18.3.1)(react@18.3.1):
react-svg@16.1.34(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.25.7
'@tanem/svg-injector': 10.1.68
@@ -3083,6 +3256,10 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react18-json-view@0.2.8(patch_hash=tfqbi5gr5whnc64n7zptszjgcy)(react@18.3.1):
dependencies:
react: 18.3.1
react@18.3.1:
dependencies:
loose-envify: 1.4.0
@@ -3269,11 +3446,12 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
typescript-eslint@8.11.0(eslint@9.13.0)(typescript@5.6.3):
typescript-eslint@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0)(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/parser': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/utils': 8.11.0(eslint@9.13.0)(typescript@5.6.3)
'@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/parser': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/utils': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
optionalDependencies:
typescript: 5.6.3
transitivePeerDependencies:
- eslint
@@ -3301,11 +3479,11 @@ snapshots:
vite@5.4.9(@types/node@22.7.8):
dependencies:
'@types/node': 22.7.8
esbuild: 0.21.5
postcss: 8.4.47
rollup: 4.24.0
optionalDependencies:
'@types/node': 22.7.8
fsevents: 2.3.3
which@2.0.2:
+16
View File
@@ -0,0 +1,16 @@
import { ButtonHTMLAttributes, FC } from "react"
import { twMerge } from "tailwind-merge"
export const ActionButton: FC<ButtonHTMLAttributes<HTMLButtonElement>> = (
props,
) => (
<button
{...props}
className={twMerge(
"text-polkadot-500 border rounded border-polkadot-500 px-2 py-1",
"cursor-pointer select-none hover:bg-polkadot-500 hover:text-polkadot-200",
props.disabled && "opacity-50 pointer-events-none",
props.className,
)}
/>
)
+6 -2
View File
@@ -149,14 +149,18 @@ const parseValue = (value: string | Binary): Binary | NOTIN => {
return Binary.fromText(value)
}
const textDecoder = new TextDecoder("utf-8", { fatal: true })
const serializeValue = (value: Binary): string | Binary => {
const bytes = value.asBytes()
if (bytes.length === 0) return ""
if (bytes.length > 5 * 1024 * 1024) return value
return bytesToString(value)
}
const textDecoder = new TextDecoder("utf-8", { fatal: true })
export const bytesToString = (value: Binary) => {
try {
return textDecoder.decode(bytes)
return textDecoder.decode(value.asBytes())
} catch (_) {
return value.asHex()
}
+44
View File
@@ -0,0 +1,44 @@
import * as ToggleGroup from "@radix-ui/react-toggle-group"
import { FC, ReactNode } from "react"
import { twMerge } from "tailwind-merge"
export const ButtonGroup: FC<{
items: Array<{
value: string
content: ReactNode
disabled?: boolean
}>
value: string
onValueChange: (value: string) => void
btnWidth?: number
}> = ({ items, value, onValueChange, btnWidth }) => {
return (
<ToggleGroup.Root
type="single"
value={value}
onValueChange={(v) => v && onValueChange(v)}
className="inline-flex bg-polkadot-800 rounded"
>
{items.map(({ value, content, disabled }) => (
<ToggleGroup.Item
value={value}
key={value}
className={twMerge(
"bg-white text-polkadot-500 ml-[1px] px-2",
"first:ml-0 first:rounded-l",
"last:rounded-r",
"hover:bg-polkadot-100",
"data-[state=on]:bg-polkadot-500 data-[state=on]:text-white",
disabled && "opacity-50 pointer-events-none",
)}
disabled={disabled}
style={{
minWidth: btnWidth,
}}
>
{content}
</ToggleGroup.Item>
))}
</ToggleGroup.Root>
)
}
+69
View File
@@ -0,0 +1,69 @@
import { CopyText } from "@/components/Copy"
import { BinaryEdit } from "@/components/Icons"
import { NOTIN } from "@codec-components"
import { Binary, HexString } from "@polkadot-api/substrate-bindings"
import { FC, useState } from "react"
import { twMerge } from "tailwind-merge"
import { BinaryEditModal } from "./BinaryEditModal"
const DISPLAY_MAX_LEN = 1024
const COPY_MAX_LEN = 5 * 1024 * 1024
export const BinaryDisplay: FC<{
value: Uint8Array | HexString | null
isEmpty: boolean
onValueChanged: (value: any | NOTIN) => boolean
decode: (value: Uint8Array | HexString) => any | NOTIN
}> = ({ value, isEmpty, onValueChanged, decode }) => {
const [binaryOpen, setBinaryOpen] = useState(false)
const displayLength = DISPLAY_MAX_LEN * 2 + 2
const copyLength = COPY_MAX_LEN * 2 + 2
const hex = value
? typeof value === "string"
? value.slice(0, copyLength)
: Binary.fromBytes(value.slice(0, COPY_MAX_LEN)).asHex()
: null
const exceedsDisplayLength = hex && hex.length > displayLength
const exceedsCopyLength = !!hex && hex.length >= COPY_MAX_LEN * 2 + 2
const copyDisabled = hex === null || exceedsCopyLength
return (
<div className="px-2 w-full">
<div className="px-3 py-2 gap-2 rounded flex flex-row items-center bg-polkadot-800">
<CopyText text={hex ?? ""} disabled={copyDisabled} />
<div className="text-sm tabular-nums max-h-12 overflow-hidden whitespace-nowrap text-ellipsis flex-1">
{hex ? (
hex.slice(0, displayLength) + (exceedsDisplayLength ? "…" : "")
) : (
<div className="flex flex-row items-center gap-1 text-slate-400">
{isEmpty
? "Start by filling out your extrinsic, or enter a binary using the edit binary button at the end of this line."
: "Cannot display hex. Complete the required values."}
</div>
)}
</div>
<BinaryEdit
size={24}
className={twMerge("cursor-pointer hover:text-polkadot-300")}
onClick={() => setBinaryOpen(true)}
/>
<BinaryEditModal
status={{
encodedValue:
typeof value === "string"
? Binary.fromHex(value).asBytes()
: (value ?? undefined),
onValueChanged,
decode,
type: "complete",
}}
open={binaryOpen}
path=""
onClose={() => setBinaryOpen(false)}
/>
</div>
</div>
)
}
+45 -76
View File
@@ -1,6 +1,6 @@
import { metadata$ } from "@/chain.state"
import { CopyText } from "@/components/Copy"
import { BinaryEdit } from "@/components/Icons"
import { ActionButton } from "@/components/ActionButton"
import { ButtonGroup } from "@/components/ButtonGroup"
import { withSubscribe } from "@/components/withSuspense"
import {
CodecComponentType,
@@ -8,13 +8,13 @@ import {
NOTIN,
} from "@codec-components"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { Binary, HexString } from "@polkadot-api/substrate-bindings"
import { Binary } from "@polkadot-api/substrate-bindings"
import { state, useStateObservable } from "@react-rxjs/core"
import { FC, useState } from "react"
import { useState } from "react"
import { map } from "rxjs"
import { twMerge } from "tailwind-merge"
import { BinaryEditModal } from "./BinaryEditModal"
import { BinaryDisplay } from "./BinaryDisplay"
import { EditMode } from "./EditMode"
import { JsonMode } from "./JsonMode"
const extrinsicProps$ = state(
metadata$.pipe(
@@ -36,6 +36,7 @@ const extrinsicDecoder = extrinsicProps$.pipeState(
)
export const Extrinsics = withSubscribe(() => {
const [viewMode, setViewMode] = useState<"edit" | "json">("edit")
const extrinsicProps = useStateObservable(extrinsicProps$)
const decoder = useStateObservable(extrinsicDecoder)
@@ -52,7 +53,7 @@ export const Extrinsics = withSubscribe(() => {
: componentValue.value.encoded) ?? null
return (
<div className="flex flex-col overflow-hidden">
<div className="flex flex-col overflow-hidden gap-2">
<div>Extrinsics</div>
<BinaryDisplay
@@ -77,75 +78,43 @@ export const Extrinsics = withSubscribe(() => {
}}
/>
<EditMode
{...extrinsicProps}
value={componentValue}
onUpdate={(value) =>
setComponentValue({ type: CodecComponentType.Updated, value })
}
/>
<div className="flex flex-row justify-between px-2">
<ButtonGroup
value={viewMode}
onValueChange={setViewMode as any}
items={[
{
value: "edit",
content: "Edit",
},
{
value: "json",
content: "JSON",
disabled: !binaryValue,
},
]}
/>
<ActionButton disabled>Submit extrinsic</ActionButton>
</div>
{viewMode === "edit" ? (
<EditMode
{...extrinsicProps}
value={componentValue}
onUpdate={(value) =>
setComponentValue({ type: CodecComponentType.Updated, value })
}
/>
) : (
<JsonMode
value={
typeof binaryValue === "string"
? Binary.fromHex(binaryValue).asBytes()
: binaryValue
}
decode={decoder}
/>
)}
</div>
)
})
const DISPLAY_MAX_LEN = 1024
const COPY_MAX_LEN = 5 * 1024 * 1024
const BinaryDisplay: FC<{
value: Uint8Array | HexString | null
isEmpty: boolean
onValueChanged: (value: any | NOTIN) => boolean
decode: (value: Uint8Array | HexString) => any | NOTIN
}> = ({ value, isEmpty, onValueChanged, decode }) => {
const [binaryOpen, setBinaryOpen] = useState(false)
const displayLength = DISPLAY_MAX_LEN * 2 + 2
const copyLength = COPY_MAX_LEN * 2 + 2
const hex = value
? typeof value === "string"
? value.slice(0, copyLength)
: Binary.fromBytes(value.slice(0, COPY_MAX_LEN)).asHex()
: null
const exceedsDisplayLength = hex && hex.length > displayLength
const exceedsCopyLength = !!hex && hex.length >= COPY_MAX_LEN * 2 + 2
const copyDisabled = hex === null || exceedsCopyLength
return (
<div className="p-2 w-full">
<div className="px-3 py-2 gap-2 rounded flex flex-row items-center bg-polkadot-800">
<CopyText text={hex ?? ""} disabled={copyDisabled} />
<div className="text-sm tabular-nums max-h-12 overflow-hidden whitespace-nowrap text-ellipsis flex-1">
{hex ? (
hex.slice(0, displayLength) + (exceedsDisplayLength ? "…" : "")
) : (
<div className="flex flex-row items-center gap-1 text-slate-400">
{isEmpty
? "Start by filling out your extrinsic, or enter a binary using the edit binary button at the end of this line."
: "Cannot display hex. Complete the required values."}
</div>
)}
</div>
<BinaryEdit
size={24}
className={twMerge("cursor-pointer hover:text-polkadot-300")}
onClick={() => setBinaryOpen(true)}
/>
<BinaryEditModal
status={{
encodedValue:
typeof value === "string"
? Binary.fromHex(value).asBytes()
: (value ?? undefined),
onValueChanged,
decode,
type: "complete",
}}
open={binaryOpen}
path=""
onClose={() => setBinaryOpen(false)}
/>
</div>
</div>
)
}
+58
View File
@@ -0,0 +1,58 @@
import { bytesToString } from "@/components/BinaryInput"
import { Binary } from "@polkadot-api/substrate-bindings"
import { FC, useMemo } from "react"
import ReactJson from "react18-json-view"
import "react18-json-view/src/style.css"
import "react18-json-view/src/dark.css"
import "./jsonView.css"
export const JsonMode: FC<{
decode: (value: Uint8Array) => unknown
value: Uint8Array | null
}> = ({ decode, value }) => {
const src = useMemo(
() => (value ? decode(value) : null),
// value
// ? JSON.parse(
// JSON.stringify(decode(value), (_, v) =>
// typeof v === "bigint"
// ? `${v}n`
// : v instanceof Binary
// ? bytesToString(v)
// : v,
// ),
// )
// : null,
[value, decode],
)
console.log(src)
return (
<div className="overflow-auto p-2 bg-polkadot-900 text-sm">
<ReactJson
src={src}
dark
theme="a11y"
// customizeNode={({ node }) => {
// if (node instanceof Binary) {
// return <>{node.asHex()}</>
// }
// }}
replacer={(v) => (v instanceof Binary ? bytesToString(v) : v)}
customizeCopy={(v) =>
JSON.stringify(
v,
(_, v) =>
typeof v === "bigint"
? `${v}n`
: v instanceof Binary
? bytesToString(v)
: v,
2,
)
}
/>
</div>
)
}
+9
View File
@@ -0,0 +1,9 @@
.json-view .json-view--copy {
opacity: 0.4;
}
.json-view .json-view--copy:hover {
opacity: 1;
}
.json-view .jv-button {
padding: 0 0.3rem;
}