feat: initial Pezkuwi Apps rebrand from polkadot-apps

Rebranded terminology:
- Polkadot → Pezkuwi
- Kusama → Dicle
- Westend → Zagros
- Rococo → PezkuwiChain
- Substrate → Bizinikiwi
- parachain → teyrchain

Custom logos with Kurdistan brand colors (#e6007a → #86e62a):
- bizinikiwi-hexagon.svg
- sora-bizinikiwi.svg
- hezscanner.svg
- heztreasury.svg
- pezkuwiscan.svg
- pezkuwistats.svg
- pezkuwiassembly.svg
- pezkuwiholic.svg
This commit is contained in:
2026-01-07 13:05:27 +03:00
commit d21bfb1320
5867 changed files with 329019 additions and 0 deletions
@@ -0,0 +1,68 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { MultiAddress } from '@pezkuwi/types/interfaces';
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { InputAddress } from '@pezkuwi/react-components';
import { keyring } from '@pezkuwi/ui-keyring';
import Bare from './Bare.js';
import Enum from './Enum.js';
function isValidAddress (value?: string | null): boolean {
if (value) {
try {
keyring.decodeAddress(value);
return true;
} catch (err) {
console.error(err);
}
}
return false;
}
function Account (props: Props): React.ReactElement<Props> {
const { className = '', defaultValue: { value }, isDisabled, isError, label, onChange, type, withLabel } = props;
const [defaultValue] = useState(() => (value as string)?.toString());
const _onChange = useCallback(
(value?: string | null) =>
onChange && onChange({
isValid: isValidAddress(value),
value
}),
[onChange]
);
// special handling for MultiAddress
if (type.type === 'MultiAddress') {
if (!isDisabled || !value || (value as MultiAddress).type !== 'Id') {
return <Enum {...props} />;
}
}
return (
<Bare className={className}>
<InputAddress
className='full'
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
isInput
label={label}
onChange={_onChange}
placeholder='5GLFK...'
type='allPlus'
withEllipsis
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Account);
@@ -0,0 +1,86 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BitLength } from '@pezkuwi/react-components/types';
import type { Registry, TypeDef } from '@pezkuwi/types/types';
import type { BN } from '@pezkuwi/util';
import type { Props } from '../types.js';
import React, { useCallback, useMemo } from 'react';
import { Input, InputNumber } from '@pezkuwi/react-components';
import { bnToBn, formatNumber, isUndefined } from '@pezkuwi/util';
import Bare from './Bare.js';
function getBitLength (registry: Registry, { type }: TypeDef): BitLength {
try {
return registry.createType(type as 'u32').bitLength() as BitLength;
} catch {
return 32;
}
}
function Amount ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, onEnter, registry, type, withLabel }: Props): React.ReactElement<Props> {
const isSigned = useMemo(
// Allow signed inputs for i{8, 16, 32, 64, 128, ...} types
() => /^i\d*$/.test(type.type),
[type]
);
const defaultValue = useMemo(
() => isDisabled
? value instanceof registry.createClass('AccountIndex')
? value.toString()
: formatNumber(value as number)
: bnToBn((value as number) || 0).toString(),
[isDisabled, registry, value]
);
const bitLength = useMemo(
() => getBitLength(registry, type),
[registry, type]
);
const _onChange = useCallback(
(value?: BN) =>
onChange && onChange({
isValid: !isUndefined(value),
value
}),
[onChange]
);
return (
<Bare className={className}>
{isDisabled
? (
<Input
className='full'
defaultValue={defaultValue}
isDisabled
label={label}
withEllipsis
withLabel={withLabel}
/>
)
: (
<InputNumber
bitLength={bitLength}
className='full'
defaultValue={defaultValue}
isError={isError}
isSigned={isSigned}
isZeroable
label={label}
onChange={_onChange}
onEnter={onEnter}
withLabel={withLabel}
/>
)
}
</Bare>
);
}
export default React.memo(Amount);
@@ -0,0 +1,160 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ParamDef, Props, RawParam } from '../types.js';
import React, { useCallback, useEffect, useState } from 'react';
import { Button } from '@pezkuwi/react-components';
import { BTreeMap } from '@pezkuwi/types';
import { isCodec, isUndefined } from '@pezkuwi/util';
import Params from '../index.js';
import getInitValue from '../initValue.js';
import { useTranslation } from '../translate.js';
import Base from './Base.js';
import useParamDefs from './useParamDefs.js';
function getParamType ([key, value]: ParamDef[]): ParamDef[] {
return [{
name: '(Key, Value)',
type: {
info: 17,
sub: [key.type, value.type],
type: `(${key.type.type}, ${value.type.type})`
}
}];
}
function getParam ([{ name, type }]: ParamDef[], index: number): ParamDef {
return {
name: `${index}: ${name || type.type}`,
type
};
}
export function getParams (keyValueParam: ParamDef[], prev: ParamDef[], max: number): ParamDef[] {
if (prev.length === max) {
return prev;
}
const params: ParamDef[] = [];
for (let index = 0; index < max; index++) {
params.push(getParam(keyValueParam, index));
}
return params;
}
export function getValues ({ isValid, value }: RawParam): RawParam[] {
return (isValid && isCodec(value) && value instanceof BTreeMap)
? [...value.entries()].map(([key, value]: RawParam[]) => {
return {
isValid: true,
value: [{ isValid: true, value: key }, { isValid: true, value }]
};
})
: [];
}
function BTreeMapParam ({ className = '', defaultValue, isDisabled = false, label, onChange, overrides, registry, type, withLabel }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const keyValueParam = getParamType(useParamDefs(registry, type));
const [values, setValues] = useState<RawParam[]>(() => getValues(defaultValue));
const [count, setCount] = useState(() => values.length);
const [params, setParams] = useState<ParamDef[]>(() => getParams(keyValueParam, [], count));
// build up the list of parameters we are using
useEffect((): void => {
keyValueParam.length &&
setParams((prev) =>
getParams(keyValueParam, prev, isDisabled ? values.length : count)
);
}, [count, values, isDisabled, keyValueParam]);
// when !isDisable, generating an input list based on count
useEffect((): void => {
!isDisabled && keyValueParam.length &&
setValues((values): RawParam[] => {
if (values.length === count) {
return values;
}
while (values.length < count) {
const value = getInitValue(registry, keyValueParam[0].type);
values.push({ isValid: !isUndefined(value), value });
}
return values.slice(0, count);
});
}, [count, defaultValue, keyValueParam, isDisabled, registry]);
// when our values has changed, alert upstream
useEffect((): void => {
const output = new Map();
let isValid = true;
for (const entry of values) {
const [key, value] = entry.value as RawParam[];
if (output.has(key)) {
isValid = false;
console.error('BTreeMap: Duplicate key ', key);
}
output.set(key, value);
isValid = isValid && entry.isValid;
}
onChange && onChange({
isValid,
value: output
});
}, [values, onChange]);
const _rowAdd = useCallback(
(): void => setCount((count) => count + 1),
[]
);
const _rowRemove = useCallback(
(): void => setCount((count) => count - 1),
[]
);
return (
<Base
className={className}
isOuter
label={label}
withLabel={withLabel}
>
{!isDisabled && (
<div className='ui--Param-BTreeMap-buttons'>
<Button
icon='plus'
label={t('Add item')}
onClick={_rowAdd}
/>
<Button
icon='minus'
isDisabled={values.length === 0}
label={t('Remove item')}
onClick={_rowRemove}
/>
</div>
)}
<Params
isDisabled={isDisabled}
onChange={setValues}
overrides={overrides}
params={params}
registry={registry}
values={values}
/>
</Base>
);
}
export default React.memo(BTreeMapParam);
@@ -0,0 +1,51 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
// circular dep :(
import InputBalance from '@pezkuwi/react-components/InputBalance';
import { BN } from '@pezkuwi/util';
import Bare from './Bare.js';
function Balance ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, onEnter, onEscape, withLabel }: Props): React.ReactElement<Props> {
const [isValid, setIsValid] = useState(false);
const [defaultValue] = useState(() => new BN((value as BN || '0').toString()).toString(10));
const _onChange = useCallback(
(value?: BN): void => {
const isValid = !isError && !!value;
onChange && onChange({
isValid,
value
});
setIsValid(isValid);
},
[isError, onChange]
);
return (
<Bare className={className}>
<InputBalance
className='full'
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
withEllipsis
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Balance);
export { Balance };
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
interface Props {
children?: React.ReactNode;
className?: string;
}
function Bare ({ children, className = '' }: Props): React.ReactElement<Props> {
return (
<div className={`${className} ui--row --relative`}>
{children}
</div>
);
}
export default React.memo(Bare);
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Size } from '../types.js';
import React from 'react';
import { Labelled } from '@pezkuwi/react-components';
import Bare from './Bare.js';
interface Props {
children?: React.ReactNode;
className?: string;
isDisabled?: boolean;
isOuter?: boolean;
label?: React.ReactNode;
labelExtra?: React.ReactNode;
size?: Size;
withLabel?: boolean;
}
function Base ({ children, className = '', isOuter, label, labelExtra, size = 'full', withLabel }: Props): React.ReactElement<Props> {
return (
<Bare className={className}>
<Labelled
className={size}
isOuter
label={label}
labelExtra={labelExtra}
withEllipsis
withLabel={withLabel}
>
{!isOuter && children}
</Labelled>
{isOuter && children}
</Bare>
);
}
export default React.memo(Base);
@@ -0,0 +1,160 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { RawParam, RawParamOnChange, RawParamOnEnter, RawParamOnEscape, Size, TypeDefExt } from '../types.js';
import React, { useCallback, useState } from 'react';
import { CopyButton, IdentityIcon, Input, styled } from '@pezkuwi/react-components';
import { compactAddLength, hexToU8a, isAscii, isHex, stringToU8a, u8aToHex, u8aToString, u8aToU8a } from '@pezkuwi/util';
import { decodeAddress } from '@pezkuwi/util-crypto';
import { useTranslation } from '../translate.js';
import Bare from './Bare.js';
interface Props {
asHex?: boolean;
children?: React.ReactNode;
className?: string;
defaultValue: RawParam;
isDisabled?: boolean;
isError?: boolean;
label?: React.ReactNode;
labelExtra?: React.ReactNode;
length?: number;
name?: string;
onChange?: RawParamOnChange;
onEnter?: RawParamOnEnter;
onEscape?: RawParamOnEscape;
size?: Size;
type: TypeDefExt;
validate?: (u8a: Uint8Array) => boolean;
withCopy?: boolean;
withLabel?: boolean;
withLength?: boolean;
}
interface Validity {
isAddress: boolean;
isValid: boolean;
lastValue?: Uint8Array;
}
const defaultValidate = (): boolean =>
true;
function convertInput (value: string): [boolean, boolean, Uint8Array] {
if (value === '0x') {
return [true, false, new Uint8Array([])];
} else if (value.startsWith('0x')) {
try {
return [true, false, isHex(value) ? hexToU8a(value) : stringToU8a(value)];
} catch {
return [false, false, new Uint8Array([])];
}
}
// maybe it is an ss58?
try {
return [true, true, decodeAddress(value)];
} catch {
// we continue
}
return isAscii(value)
? [true, false, stringToU8a(value)]
: [value === '0x', false, new Uint8Array([])];
}
function BaseBytes ({ asHex, children, className = '', defaultValue: { value }, isDisabled, isError, label, labelExtra, length = -1, onChange, onEnter, onEscape, size = 'full', validate = defaultValidate, withCopy, withLabel, withLength }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [defaultValue] = useState(
(): string | undefined => {
if (value) {
const u8a = u8aToU8a(value as Uint8Array);
return isAscii(u8a)
? u8aToString(u8a)
: u8aToHex(u8a);
}
return undefined;
}
);
const [{ isAddress, isValid, lastValue }, setValidity] = useState<Validity>(() => ({
isAddress: false,
isValid: isHex(defaultValue) || isAscii(defaultValue)
}));
const _onChange = useCallback(
(hex: string): void => {
let [isValid, isAddress, value] = convertInput(hex);
isValid = isValid && validate(value) && (
length !== -1
? value.length === length
: (value.length !== 0 || hex === '0x')
);
if (withLength && isValid) {
value = compactAddLength(value);
}
onChange && onChange({
isValid,
value: asHex
? u8aToHex(value)
: value
});
setValidity({ isAddress, isValid, lastValue: value });
},
[asHex, length, onChange, validate, withLength]
);
return (
<StyledBare className={className}>
<Input
className={size}
defaultValue={defaultValue}
isAction={!!children}
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
labelExtra={labelExtra}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
placeholder={t('0x prefixed hex, e.g. 0x1234 or ascii data')}
type='text'
withEllipsis
withLabel={withLabel}
>
{children}
{withCopy && (
<CopyButton value={defaultValue} />
)}
{isAddress && (
<IdentityIcon
className='ui--InputAddressSimpleIcon'
size={32}
value={lastValue}
/>
)}
</Input>
</StyledBare>
);
}
const StyledBare = styled(Bare)`
.ui--InputAddressSimpleIcon {
background: #eee;
border: 1px solid #888;
border-radius: 50%;
left: -16px;
position: absolute;
top: 8px;
}
`;
export default React.memo(BaseBytes);
@@ -0,0 +1,19 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React from 'react';
import BasicAccountIdBase from './BasicAccountIdBase.js';
function BasicAccountId20 (props: Props): React.ReactElement<Props> {
return (
<BasicAccountIdBase
{...props}
bytesLength={20}
/>
);
}
export default React.memo(BasicAccountId20);
@@ -0,0 +1,19 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React from 'react';
import BasicAccountIdBase from './BasicAccountIdBase.js';
function BasicAccountId32 (props: Props): React.ReactElement<Props> {
return (
<BasicAccountIdBase
{...props}
bytesLength={32}
/>
);
}
export default React.memo(BasicAccountId32);
@@ -0,0 +1,62 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props as BaseProps } from '../types.js';
import React, { useCallback, useState } from 'react';
import { InputAddressSimple } from '@pezkuwi/react-components';
import { isEthereumAddress, validateAddress } from '@pezkuwi/util-crypto';
import Bare from './Bare.js';
interface Props extends BaseProps {
bytesLength: 20 | 32;
}
function isValidAddress (value: string | null | undefined, isEthereum: boolean): boolean {
if (value) {
try {
return isEthereum
? isEthereumAddress(value)
: validateAddress(value);
} catch (err) {
console.error(err);
}
}
return false;
}
function BasicAccountIdBase (props: Props): React.ReactElement<Props> {
const { bytesLength, className = '', defaultValue: { value }, isDisabled, isError, label, onChange } = props;
const [defaultValue] = useState(() => (value as string)?.toString());
const _onChange = useCallback(
(value?: string | null) =>
onChange && onChange({
isValid: isValidAddress(value, bytesLength === 20),
value
}),
[bytesLength, onChange]
);
return (
<Bare className={className}>
<InputAddressSimple
bytesLength={bytesLength}
className='full'
defaultValue={defaultValue}
forceIconType={bytesLength === 20 ? 'ethereum' : 'bizinikiwi'}
isDisabled={isDisabled}
isError={isError}
label={label}
noConvert
onChange={_onChange}
placeholder={bytesLength === 20 ? '0x1...' : '5...'}
/>
</Bare>
);
}
export default React.memo(BasicAccountIdBase);
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useRef, useState } from 'react';
import { Dropdown } from '@pezkuwi/react-components';
import { isBoolean } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
import Bare from './Bare.js';
function BoolParam ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, withLabel }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [defaultValue] = useState(
value instanceof Boolean
? value.valueOf()
: isBoolean(value)
? value
: false
);
const options = useRef([
{ text: t('No'), value: false },
{ text: t('Yes'), value: true }
]);
const _onChange = useCallback(
(value: boolean) =>
onChange && onChange({
isValid: true,
value
}),
[onChange]
);
return (
<Bare className={className}>
<Dropdown
className='full'
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={_onChange}
options={options.current}
withEllipsis
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(BoolParam);
+78
View File
@@ -0,0 +1,78 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Toggle } from '@pezkuwi/react-components';
import { compactAddLength } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
import BaseBytes from './BaseBytes.js';
import File from './File.js';
function Bytes ({ className = '', defaultValue, isDisabled, isError, label, name, onChange, onEnter, onEscape, type, withLabel, withLength = true }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isValid, setIsValid] = useState(false);
const [isFileDrop, setFileInput] = useState(false);
const _onChangeFile = useCallback(
(value: Uint8Array): void => {
const isValid = value.length !== 0;
onChange && onChange({
isValid,
value: compactAddLength(value)
});
setIsValid(isValid);
},
[onChange]
);
const toggleLabel = !isDisabled && (
<Toggle
label={t('file upload')}
onChange={setFileInput}
value={isFileDrop}
/>
);
return (
<div className={`${className} --relative`}>
{!isDisabled && isFileDrop
? (
<File
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
labelExtra={toggleLabel}
onChange={_onChangeFile}
withLabel={withLabel}
/>
)
: (
<BaseBytes
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
labelExtra={toggleLabel}
length={-1}
name={name}
onChange={onChange}
onEnter={onEnter}
onEscape={onEscape}
type={type}
withLabel={withLabel}
withLength={withLength}
/>
)
}
{}
</div>
);
}
export default React.memo(Bytes);
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Extrinsic } from '@pezkuwi/types/interfaces';
import type { Props } from '../types.js';
import React from 'react';
import { Static } from '@pezkuwi/react-components';
import { Call } from '../Named/index.js';
import { useTranslation } from '../translate.js';
import Bare from './Bare.js';
import Unknown from './Unknown.js';
function CallDisplay (props: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { className = '', defaultValue: { value }, isDisabled, label, withLabel } = props;
if (!isDisabled) {
return (
<Unknown {...props} />
);
}
const call = value as Extrinsic;
const { method, section } = call.registry.findMetaCall(call.callIndex);
const callName = `${section}.${method}`;
return (
<Bare>
<Static
className={`${className} full`}
label={label}
withLabel={withLabel}
>
{callName}
</Static>
<Call
callName={callName}
labelHash={t('call hash / {{section}}.{{method}}', { replace: { method, section } })}
value={call}
withHash
/>
</Bare>
);
}
export default React.memo(CallDisplay);
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid } from '@pezkuwi/types/lookup';
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Input } from '@pezkuwi/react-components';
import { isCodec } from '@pezkuwi/util';
import { fromIpfsCid, toIpfsCid } from '../util.js';
import Bare from './Bare.js';
import Static from './Static.js';
import Struct from './Struct.js';
function Cid (props: Props): React.ReactElement<Props> {
const { className = '', defaultValue, isDisabled, isError, label, onChange, withLabel } = props;
const [isValid, setIsValid] = useState(false);
const [ipfsCid] = useState<string | null>(() =>
isDisabled && defaultValue && isCodec(defaultValue.value)
? toIpfsCid(defaultValue.value as PalletAllianceCid)
: null
);
const [isStruct] = useState<boolean>(() => isDisabled || !defaultValue || isCodec(defaultValue.value));
const _onChange = useCallback(
(_value: string): void => {
const value = fromIpfsCid(_value);
const isValid = !!value;
onChange && onChange({
isValid,
value
});
setIsValid(isValid);
},
[onChange]
);
if (ipfsCid) {
return (
<Static {...props}>
<Input
className='full'
isDisabled
label='ipfs'
type='text'
value={ipfsCid}
withLabel={withLabel}
/>
</Static>
);
} else if (isStruct) {
return <Struct {...props} />;
}
return (
<Bare className={className}>
<Input
className='full'
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
placeholder='IPFS compatible CID'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Cid);
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { isWasm } from '@pezkuwi/util';
import Bytes from './Bytes.js';
import BytesFile from './File.js';
function Code ({ className = '', defaultValue, isDisabled, isError, label, onChange, onEnter, onEscape, registry, type, withLabel }: Props): React.ReactElement<Props> {
const [isValid, setIsValid] = useState(false);
const _onChange = useCallback(
(value: Uint8Array): void => {
const isValid = isWasm(value);
onChange && onChange({ isValid, value });
setIsValid(isValid);
},
[onChange]
);
if (isDisabled) {
return (
<Bytes
className={className}
defaultValue={defaultValue}
isError={isError || !isValid}
label={label}
onEnter={onEnter}
onEscape={onEscape}
registry={registry}
type={type}
withLabel={withLabel}
/>
);
}
return (
<BytesFile
className={className}
defaultValue={defaultValue}
isError={isError || !isValid}
label={label}
onChange={_onChange}
withLabel={withLabel}
/>
);
}
export default React.memo(Code);
@@ -0,0 +1,87 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { DispatchError } from '@pezkuwi/types/interfaces';
import type { Props as BaseProps } from '../types.js';
import React, { useEffect, useState } from 'react';
import { Input } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import Static from './Static.js';
import Unknown from './Unknown.js';
interface Details {
details?: string | null;
type?: string;
}
interface Props extends BaseProps {
childrenPre?: React.ReactNode;
}
function isDispatchError (value?: unknown): value is DispatchError {
return !!(value && (
(value as DispatchError).isModule ||
(value as DispatchError).isToken
));
}
function ErrorDisplay (props: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [{ details, type }, setDetails] = useState<Details>({});
useEffect((): void => {
const { value } = props.defaultValue || {};
if (isDispatchError(value)) {
if (value.isModule) {
try {
const mod = value.asModule;
const { docs, name, section } = mod.registry.findMetaError(mod);
return setDetails({
details: docs.join(', '),
type: `${section}.${name}`
});
} catch (error) {
// Errors may not actually be exposed, in this case, just return the default representation
console.error(error);
}
} else if (value.isToken) {
return setDetails({
details: value.asToken.type,
type: value.type
});
}
}
setDetails({ details: null });
}, [props.defaultValue]);
if (!props.isDisabled || !details) {
return <Unknown {...props} />;
}
return (
<Static {...props}>
<Input
className='full'
isDisabled
label={t('type')}
value={type}
/>
{details && (
<Input
className='full'
isDisabled
label={t('details')}
value={details}
/>
)}
</Static>
);
}
export default React.memo(ErrorDisplay);
@@ -0,0 +1,56 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { DispatchResult } from '@pezkuwi/types/interfaces';
import type { Props } from '../types.js';
import React, { useMemo } from 'react';
import { Input } from '@pezkuwi/react-components';
import DispatchError from './DispatchError.js';
import Static from './Static.js';
import Unknown from './Unknown.js';
function isDispatchResultErr (value?: unknown): value is DispatchResult {
return !!(value && (value as DispatchResult).isErr);
}
function DispatchResultDisplay (props: Props): React.ReactElement<Props> {
const { defaultValue, isDisabled, label } = props;
const dispatchError = useMemo(
() => defaultValue && isDispatchResultErr(defaultValue.value)
? { isValid: true, value: defaultValue.value.asErr }
: null,
[defaultValue]
);
if (!isDisabled) {
return <Unknown {...props} />;
} else if (!dispatchError) {
return (
<Static
{...props}
defaultValue={{ isValid: true, value: 'Ok' }}
/>
);
}
return (
<DispatchError
{...props}
childrenPre={
<Input
className='full'
isDisabled
label={label}
value='Err'
/>
}
defaultValue={dispatchError}
label='DispatchError'
/>
);
}
export default React.memo(DispatchResultDisplay);
+169
View File
@@ -0,0 +1,169 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Registry, TypeDef } from '@pezkuwi/types/types';
import type { ParamDef, Props, RawParam } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Dropdown } from '@pezkuwi/react-components';
import { Enum, getTypeDef } from '@pezkuwi/types';
import { isObject } from '@pezkuwi/util';
import Params from '../index.js';
import Bare from './Bare.js';
interface Option {
text?: string;
value?: string;
}
interface Options {
options: Option[];
subTypes: TypeDef[];
}
interface Initial {
initialEnum: string | undefined | null;
initialParams: RawParam[] | undefined | null;
}
function getSubTypes (registry: Registry, type: TypeDef): TypeDef[] {
return getTypeDef(
registry.createType(type.type as '(u32, u32)').toRawType()
).sub as TypeDef[];
}
function getOptions (registry: Registry, type: TypeDef): Options {
const subTypes = getSubTypes(registry, type).filter(({ name }) =>
!!name &&
!name.startsWith('__Unused')
);
return {
options: subTypes.map(({ name }): Option => ({
text: name,
value: name
})),
subTypes
};
}
function getInitial (defaultValue: RawParam, options: Option[]): Initial {
if (defaultValue?.value) {
if (defaultValue.value instanceof Enum) {
return {
initialEnum: defaultValue.value.type,
initialParams: [{
isValid: true,
value: defaultValue.value.inner
}]
};
} else if (isObject<Record<string, unknown>>(defaultValue.value)) {
const [initialEnum, value] = Object.entries(defaultValue.value)[0];
// Ensure that the defaultValue is actually in our enum, e.g. it
// may start with __Unused<x> values, in which case it would be
// invalid
if (options.some(({ value }) => value === initialEnum)) {
return {
initialEnum,
initialParams: [{
isValid: true,
value
}]
};
}
}
}
return {
initialEnum: options[0]?.value,
initialParams: undefined
};
}
function getCurrent (registry: Registry, type: TypeDef, defaultValue: RawParam, subTypes: TypeDef[]): ParamDef[] | null {
const subs = getSubTypes(registry, type);
return defaultValue.value instanceof Enum
? [{ name: defaultValue.value.type, type: subs[defaultValue.value.index] }]
: [{ name: subTypes[0].name, type: subTypes[0] }];
}
function EnumParam (props: Props): React.ReactElement<Props> {
const { className = '', defaultValue, isDisabled, isError, label, onChange, overrides, registry, type, withLabel } = props;
const [{ options, subTypes }] = useState<Options>(() => getOptions(registry, type));
const [current, setCurrent] = useState<ParamDef[] | null>(() => getCurrent(registry, type, defaultValue, subTypes));
const [{ initialEnum, initialParams }, setInitial] = useState<Initial>(() => getInitial(defaultValue, options));
const _onChange = useCallback(
(value: string): void => {
if (isDisabled) {
return;
}
const newType = subTypes.find(({ name }) => name === value) || null;
setCurrent(
newType
? [{ name: newType.name, type: newType }]
: null
);
if (newType) {
// if the enum changes, we want to discard the original initParams,
// these are not applicable anymore, rather use empty defaults
setInitial((prev) =>
newType.name === prev.initialEnum
? prev
: { initialEnum: prev.initialEnum, initialParams: null }
);
}
},
[isDisabled, subTypes]
);
const _onChangeParam = useCallback(
([{ isValid, value }]: RawParam[]): void => {
if (isDisabled) {
return;
}
current && onChange && onChange({
isValid,
value: { [current[0].name || 'unknown']: value }
});
},
[current, isDisabled, onChange]
);
return (
<Bare className={className}>
<Dropdown
className='full'
defaultValue={initialEnum}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={_onChange}
options={options}
withEllipsis
withLabel={withLabel}
/>
{current && (
<Params
isDisabled={isDisabled}
isError={isError}
onChange={_onChangeParam}
overrides={overrides}
params={current}
registry={registry}
values={initialParams}
/>
)}
</Bare>
);
}
export default React.memo(EnumParam);
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import { InputFile } from '@pezkuwi/react-components';
import Bare from './Bare.js';
interface Props {
className?: string;
defaultValue?: unknown;
isDisabled?: boolean;
isError?: boolean;
label?: React.ReactNode;
labelExtra?: React.ReactNode;
onChange?: (contents: Uint8Array) => void;
placeholder?: string;
withLabel?: boolean;
}
function File ({ className = '', isDisabled, isError = false, label, labelExtra, onChange, placeholder, withLabel }: Props): React.ReactElement<Props> {
return (
<Bare className={className}>
<InputFile
isDisabled={isDisabled}
isError={isError}
label={label}
labelExtra={labelExtra}
onChange={onChange}
placeholder={placeholder}
withEllipsis
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(File);
@@ -0,0 +1,31 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React from 'react';
import BaseBytes from './BaseBytes.js';
function Hash160 ({ className = '', defaultValue, isDisabled, isError, label, name, onChange, onEnter, onEscape, type, withLabel }: Props): React.ReactElement<Props> {
return (
<BaseBytes
asHex
className={className}
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
length={20}
name={name}
onChange={onChange}
onEnter={onEnter}
onEscape={onEscape}
type={type}
withCopy={isDisabled}
withLabel={withLabel}
/>
);
}
export default React.memo(Hash160);
@@ -0,0 +1,86 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Toggle } from '@pezkuwi/react-components';
import { u8aToHex } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
import BaseBytes from './BaseBytes.js';
import File from './File.js';
function Hash256 ({ className = '', defaultValue, isDisabled, isError, label, name, onChange, onEnter, onEscape, registry, type, withLabel }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isFileDrop, setFileInput] = useState(false);
const [placeholder, setPlaceholder] = useState<string | null>(null);
const _onChangeFile = useCallback(
(u8a: Uint8Array): void => {
const value = registry.hash(u8a);
setPlaceholder(u8aToHex(value));
onChange && onChange({
isValid: true,
value
});
},
[onChange, registry]
);
const _setFileInput = useCallback(
(value: boolean): void => {
setPlaceholder(null);
setFileInput(value);
},
[setFileInput, setPlaceholder]
);
const toggleLabel = !isDisabled && (
<Toggle
label={t('hash a file')}
onChange={_setFileInput}
value={isFileDrop}
/>
);
return (
<div className={className}>
{!isDisabled && isFileDrop
? (
<File
isDisabled={isDisabled}
isError={isError}
label={label}
labelExtra={toggleLabel}
onChange={_onChangeFile}
placeholder={placeholder || undefined}
withLabel={withLabel}
/>
)
: (
<BaseBytes
asHex
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
labelExtra={toggleLabel}
length={32}
name={name}
onChange={onChange}
onEnter={onEnter}
onEscape={onEscape}
type={type}
withCopy={isDisabled}
withLabel={withLabel}
/>
)
}
</div>
);
}
export default React.memo(Hash256);
@@ -0,0 +1,31 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React from 'react';
import BaseBytes from './BaseBytes.js';
function Hash512 ({ className = '', defaultValue, isDisabled, isError, label, name, onChange, onEnter, onEscape, type, withLabel }: Props): React.ReactElement<Props> {
return (
<BaseBytes
asHex
className={className}
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
length={64}
name={name}
onChange={onChange}
onEnter={onEnter}
onEscape={onEscape}
type={type}
withCopy={isDisabled}
withLabel={withLabel}
/>
);
}
export default React.memo(Hash512);
@@ -0,0 +1,90 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useEffect, useState } from 'react';
import { Input } from '@pezkuwi/react-components';
import { compactAddLength, hexToU8a, u8aConcat } from '@pezkuwi/util';
import Bare from './Bare.js';
interface StateParam {
isValid: boolean;
u8a: Uint8Array;
}
// eslint-disable-next-line @typescript-eslint/ban-types
export function createParam (hex: string | String, ignoreLength = false): StateParam {
let u8a;
let isValid = false;
try {
u8a = hexToU8a(hex.toString());
isValid = ignoreLength || u8a.length !== 0;
} catch {
u8a = new Uint8Array([]);
}
return {
isValid,
u8a: compactAddLength(u8a)
};
}
function KeyValue ({ className = '', isDisabled, label, onChange, onEnter, withLabel }: Props): React.ReactElement<Props> {
const [, setIsValid] = useState(false);
const [key, setKey] = useState<StateParam>(() => ({ isValid: false, u8a: new Uint8Array([]) }));
const [value, setValue] = useState<StateParam>(() => ({ isValid: false, u8a: new Uint8Array([]) }));
useEffect((): void => {
const isValid = key.isValid && value.isValid;
onChange && onChange({
isValid,
value: u8aConcat(
key.u8a,
value.u8a
)
});
setIsValid(isValid);
}, [key, onChange, value]);
const _onChangeKey = useCallback(
(key: string): void => setKey(createParam(key)),
[]
);
const _onChangeValue = useCallback(
(value: string): void => setValue(createParam(value, true)),
[]
);
return (
<Bare className={className}>
<Input
className='medium'
isDisabled={isDisabled}
isError={!key.isValid}
label={label}
onChange={_onChangeKey}
placeholder='0x...'
type='text'
withLabel={withLabel}
/>
<Input
className='medium'
isDisabled={isDisabled}
isError={!value.isValid}
onChange={_onChangeValue}
onEnter={onEnter}
placeholder='0x...'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(KeyValue);
@@ -0,0 +1,125 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Vec } from '@pezkuwi/types';
import type { KeyValue as Pair } from '@pezkuwi/types/interfaces';
import type { Props, RawParam } from '../types.js';
import React, { useCallback, useState } from 'react';
import { assert, isHex, u8aToHex, u8aToString } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
import Base from './Base.js';
import Bytes from './Bytes.js';
import File from './File.js';
import { createParam } from './KeyValue.js';
interface Parsed {
isValid: boolean;
value: [Uint8Array, Uint8Array][];
}
const BYTES_TYPE = {
info: 0,
type: 'Bytes'
};
function parseFile (raw: Uint8Array): Parsed {
const json = JSON.parse(u8aToString(raw)) as Record<string, string>;
const keys = Object.keys(json);
let isValid = keys.length !== 0;
const value = keys.map((key): [Uint8Array, Uint8Array] => {
const value = json[key];
assert(isHex(key) && isHex(value), `Non-hex key/value pair found in ${key.toString()} => ${value.toString()}`);
const encKey = createParam(key);
const encValue = createParam(value, true);
isValid = isValid && encKey.isValid && encValue.isValid;
return [encKey.u8a, encValue.u8a];
});
return {
isValid,
value
};
}
function KeyValueArray ({ className = '', defaultValue, isDisabled, isError, label, onChange, onEnter, onEscape, registry, withLabel }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [placeholder, setPlaceholder] = useState<string>(t('click to select or drag and drop JSON key/value (hex-encoded) file'));
const _onChange = useCallback(
(raw: Uint8Array): void => {
let encoded: Parsed = { isValid: false, value: [] };
try {
encoded = parseFile(raw);
setPlaceholder(t('{{count}} key/value pairs encoded for submission', {
replace: {
count: encoded.value.length
}
}));
} catch (error) {
console.error('Error converting json k/v', error);
setPlaceholder(t('click to select or drag and drop JSON key/value (hex-encoded) file'));
}
onChange && onChange(encoded);
},
[onChange, t]
);
if (isDisabled) {
const pairs = defaultValue.value as Vec<Pair>;
return (
<>
<Base
className={className}
label={label}
>
<div />
</Base>
<div className='ui--Params'>
{pairs.map(([key, value]): React.ReactNode => {
const keyHex = u8aToHex(key.toU8a(true));
return (
<Bytes
defaultValue={{ value } as unknown as RawParam}
isDisabled
key={keyHex}
label={keyHex}
name={keyHex}
onEnter={onEnter}
onEscape={onEscape}
registry={registry}
type={BYTES_TYPE}
/>
);
})}
</div>
</>
);
}
return (
<File
className={className}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={_onChange}
placeholder={placeholder}
withLabel={withLabel}
/>
);
}
export default React.memo(KeyValueArray);
@@ -0,0 +1,52 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props, RawParamOnChangeValue } from '../types.js';
import React, { useCallback } from 'react';
import { Static } from '@pezkuwi/react-components';
import Amount from './Amount.js';
function Moment ({ className = '', defaultValue, isDisabled, isError, label, onChange, onEnter, onEscape, registry, type, withLabel }: Props): React.ReactElement<Props> {
const _onChange = useCallback(
(value: RawParamOnChangeValue) =>
onChange && onChange(value),
[onChange]
);
if (isDisabled) {
return (
<Static
className={className}
defaultValue={
(defaultValue?.value)
? (defaultValue.value as string).toString()
: ''
}
isError={isError}
label={label}
withLabel={withLabel}
/>
);
}
return (
<Amount
className={className}
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
registry={registry}
type={type}
withLabel={withLabel}
/>
);
}
export default React.memo(Moment);
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useEffect } from 'react';
function Null ({ onChange }: Props): React.ReactElement<Props> | null {
useEffect((): void => {
onChange && onChange({
isValid: true,
value: null
});
}, [onChange]);
return null;
}
export default React.memo(Null);
@@ -0,0 +1,29 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Bytes } from '@pezkuwi/types';
import type { Props } from '../types.js';
import React from 'react';
import CallDisplay from './Call.js';
import Unknown from './Unknown.js';
function OpaqueCall (props: Props): React.ReactElement<Props> {
if (!props.isDisabled) {
return (
<Unknown {...props} />
);
}
const value = props.registry.createType('Call', (props.defaultValue.value as Bytes).toHex());
return (
<CallDisplay
{...props}
defaultValue={{ isValid: true, value }}
/>
);
}
export default React.memo(OpaqueCall);
+103
View File
@@ -0,0 +1,103 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Codec, TypeDef } from '@pezkuwi/types/types';
import type { ParamDef, Props, RawParamOnChangeValue } from '../types.js';
import React, { useCallback, useEffect, useState } from 'react';
import { Toggle } from '@pezkuwi/react-components';
import { Option } from '@pezkuwi/types';
import { isU8a, u8aConcat } from '@pezkuwi/util';
import Holder from '../Holder.js';
import { useTranslation } from '../translate.js';
import Base from './Base.js';
import Param from './index.js';
import Static from './Static.js';
import useParamDefs from './useParamDefs.js';
import { getParams } from './Vector.js';
const DEF_VALUE = { isValid: true, value: undefined };
const OPT_PREFIX = new Uint8Array([1]);
function OptionDisplay ({ className = '', defaultValue: _defaultValue, isDisabled, label, onChange, onEnter, onEscape, registry, type, withLabel }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const inputParams = useParamDefs(registry, type);
const [params] = useState<ParamDef[]>(() => getParams(inputParams, [], (inputParams[0].length || 1)));
const { sub, withOptionActive } = type;
const [isActive, setIsActive] = useState(() => withOptionActive || !!(_defaultValue && _defaultValue.value instanceof Option && _defaultValue.value.isSome) || false);
const [defaultValue] = useState(
() => isActive || isDisabled
? _defaultValue && (
_defaultValue.value instanceof Option && _defaultValue.value.isSome
? { isValid: _defaultValue.isValid, value: (_defaultValue.value as Option<Codec>).unwrap() }
: DEF_VALUE
)
: DEF_VALUE
);
useEffect((): void => {
!isActive && onChange && onChange({
isValid: true,
value: null
});
}, [isActive, onChange]);
const _onChange = useCallback(
(value: RawParamOnChangeValue) =>
onChange && onChange(
value.isValid && isU8a(value.value) && !withOptionActive && isActive
? { isValid: true, value: u8aConcat(OPT_PREFIX, value.value) }
: value
),
[isActive, onChange, withOptionActive]
);
return (
<div className={`${className} --relative`}>
<Base
className='--relative'
label={label}
labelExtra={!isDisabled && (
<Toggle
label={t('include option')}
onChange={setIsActive}
value={isActive}
/>
)}
withLabel={withLabel}
/>
<Holder>
<div className='ui--Params-Content'>
{isActive
? (
<Param
defaultValue={defaultValue}
isDisabled={isDisabled || !isActive}
isOptional={!isActive && !isDisabled}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
registry={registry}
type={(sub ?? params.at(0)?.type) as TypeDef}
/>
)
: (
<Static
defaultValue={DEF_VALUE}
isOptional
label='None'
/>
)
}
</div>
</Holder>
</div>
);
}
export default React.memo(OptionDisplay);
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Codec } from '@pezkuwi/types/types';
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Input } from '@pezkuwi/react-components';
import Bare from './Bare.js';
function Raw ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, onEnter, onEscape, withLabel }: Props): React.ReactElement<Props> {
const [isValid, setIsValid] = useState(false);
const _onChange = useCallback(
(value: string): void => {
const isValid = value.length !== 0;
onChange && onChange({
isValid,
value
});
setIsValid(isValid);
},
[onChange]
);
const defaultValue = value
? ((value as { toHex?: () => unknown }).toHex ? (value as Codec).toHex() : value)
: '';
return (
<Bare className={className}>
<Input
className='full'
defaultValue={defaultValue as string}
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
placeholder='Hex data'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Raw);
@@ -0,0 +1,67 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Codec } from '@pezkuwi/types/types';
import type { RawParam } from '../types.js';
import React, { useMemo } from 'react';
import { Static, styled } from '@pezkuwi/react-components';
import { useTranslation } from '../translate.js';
import { toHumanJson } from '../valueToText.js';
import Bare from './Bare.js';
interface Props {
asHex?: boolean;
children?: React.ReactNode;
childrenPre?: React.ReactNode;
className?: string;
defaultValue: RawParam;
isOptional?: boolean;
label?: React.ReactNode;
withLabel?: boolean;
}
function StaticParam ({ asHex, children, childrenPre, className = '', defaultValue, isOptional, label }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const value = useMemo(
() => !!defaultValue?.value && (
asHex
? (defaultValue.value as Codec).toHex()
: toHumanJson(
(defaultValue.value as Codec).toHuman
? (defaultValue.value as Codec).toHuman()
: defaultValue.value
)
),
[asHex, defaultValue]
);
return (
<StyledBare className={className}>
{childrenPre}
<Static
className='full'
label={label}
value={<pre>{value || (isOptional ? <>&nbsp;</> : t('<empty>'))}</pre>}
/>
{children}
</StyledBare>
);
}
const StyledBare = styled(Bare)`
pre {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.ui--Static {
margin-bottom: 0 !important;
}
`;
export default React.memo(StaticParam);
@@ -0,0 +1,63 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props, RawParam } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Struct } from '@pezkuwi/types';
import { isCodec } from '@pezkuwi/util';
import Params from '../index.js';
import Base from './Base.js';
import useParamDefs from './useParamDefs.js';
function extractValues ({ isValid, value }: RawParam): RawParam[] | undefined {
return (isValid && isCodec(value) && value instanceof Struct)
? value.toArray().map((value) => ({ isValid: true, value }))
: undefined;
}
function StructParam (props: Props): React.ReactElement<Props> {
const params = useParamDefs(props.registry, props.type);
const { className = '', defaultValue, isDisabled, label, onChange, overrides, withLabel } = props;
const [values] = useState(() => extractValues(defaultValue));
const _onChangeParams = useCallback(
(values: RawParam[]): void => {
if (isDisabled) {
return;
}
onChange && onChange({
isValid: values.reduce((result: boolean, { isValid }) => result && isValid, true),
value: params.reduce((value: Record<string, unknown>, { name }, index): Record<string, unknown> => {
value[name || 'unknown'] = values[index].value;
return value;
}, {})
});
},
[isDisabled, params, onChange]
);
return (
<div className='ui--Params-Struct'>
<Base
className={className}
label={label}
withLabel={withLabel}
/>
<Params
isDisabled={isDisabled}
onChange={_onChangeParams}
overrides={overrides}
params={params}
registry={props.registry}
values={values}
/>
</div>
);
}
export default React.memo(StructParam);
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Input } from '@pezkuwi/react-components';
import Bare from './Bare.js';
function Text ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, onEnter, onEscape, withLabel }: Props): React.ReactElement<Props> {
const [isValid, setIsValid] = useState(false);
const _onChange = useCallback(
(value: string): void => {
const isValid = value.length !== 0;
onChange && onChange({
isValid,
value
});
setIsValid(isValid);
},
[onChange]
);
const defaultValue = (value as string || '').toString();
return (
<Bare className={className}>
<Input
className='full'
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
placeholder='<any string>'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Text);
+58
View File
@@ -0,0 +1,58 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props, RawParam } from '../types.js';
import React, { useCallback, useState } from 'react';
import { Tuple } from '@pezkuwi/types';
import Params from '../index.js';
import Base from './Base.js';
import useParamDefs from './useParamDefs.js';
function getInitialValues ({ value }: RawParam): RawParam[] {
return value instanceof Tuple
? value.map((value: unknown) => ({ isValid: true, value }))
: value as RawParam[];
}
function TupleDisplay (props: Props): React.ReactElement<Props> {
const { className = '', defaultValue, isDisabled, label, onChange, overrides, registry, type, withLabel } = props;
const params = useParamDefs(registry, type);
const [values] = useState<RawParam[]>(() => getInitialValues(defaultValue));
const _onChangeParams = useCallback(
(values: RawParam[]): void => {
if (isDisabled) {
return;
}
onChange && onChange({
isValid: values.reduce<boolean>((result, { isValid }) => result && isValid, true),
value: values.map(({ value }) => value)
});
},
[isDisabled, onChange]
);
return (
<div className='ui--Params-Tuple'>
<Base
className={className}
label={label}
withLabel={withLabel}
/>
<Params
isDisabled={isDisabled}
onChange={_onChangeParams}
overrides={overrides}
params={params}
registry={registry}
values={values}
/>
</div>
);
}
export default React.memo(TupleDisplay);
@@ -0,0 +1,40 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props as BaseProps } from '../types.js';
import React from 'react';
import BaseBytes from './BaseBytes.js';
import Static from './Static.js';
interface Props extends BaseProps {
children?: React.ReactNode;
}
function Unknown (props: Props): React.ReactElement<Props> {
const { className = '', defaultValue, isDisabled, isError, label, name, onChange, onEnter, onEscape, type } = props;
if (isDisabled) {
return <Static {...props} />;
}
return (
<BaseBytes
asHex
className={className}
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
length={-1}
name={name}
onChange={onChange}
onEnter={onEnter}
onEscape={onEscape}
type={type}
/>
);
}
export default React.memo(Unknown);
+136
View File
@@ -0,0 +1,136 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ParamDef, Props, RawParam } from '../types.js';
import React, { useCallback, useEffect, useState } from 'react';
import { Button } from '@pezkuwi/react-components';
import { isUndefined } from '@pezkuwi/util';
import Params from '../index.js';
import getInitValue from '../initValue.js';
import { useTranslation } from '../translate.js';
import Base from './Base.js';
import useParamDefs from './useParamDefs.js';
function getParam ([{ name, type }]: ParamDef[], index: number): ParamDef {
return {
name: `${index}: ${name || type.type}`,
type
};
}
export function getParams (inputParams: ParamDef[], prev: ParamDef[], max: number): ParamDef[] {
if (prev.length === max) {
return prev;
}
const params: ParamDef[] = [];
for (let index = 0; index < max; index++) {
params.push(getParam(inputParams, index));
}
return params;
}
export function getValues ({ value }: RawParam): RawParam[] {
if (value instanceof Set) {
value = [...value.values()];
}
return Array.isArray(value)
? value.map((value: RawParam) =>
isUndefined(value) || isUndefined(value.isValid)
? { isValid: !isUndefined(value), value }
: value
)
: [];
}
function Vector ({ className = '', defaultValue, isDisabled = false, label, onChange, overrides, registry, type, withLabel }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const inputParams = useParamDefs(registry, type);
const [values, setValues] = useState<RawParam[]>(() => getValues(defaultValue));
const [count, setCount] = useState(() => values.length);
const [params, setParams] = useState<ParamDef[]>(() => getParams(inputParams, [], count));
// build up the list of parameters we are using
useEffect((): void => {
inputParams.length &&
setParams((prev) =>
getParams(inputParams, prev, isDisabled ? values.length : count)
);
}, [count, values, isDisabled, inputParams]);
// when !isDisable, generating an input list based on count
useEffect((): void => {
!isDisabled && inputParams.length &&
setValues((values): RawParam[] => {
if (values.length === count) {
return values;
}
while (values.length < count) {
const value = getInitValue(registry, inputParams[0].type);
values.push({ isValid: !isUndefined(value), value });
}
return values.slice(0, count);
});
}, [count, defaultValue, inputParams, isDisabled, registry]);
// when our values has changed, alert upstream
useEffect((): void => {
onChange && onChange({
isValid: values.reduce<boolean>((result, { isValid }) => result && isValid, true),
value: values.map(({ value }) => value)
});
}, [values, onChange]);
const _rowAdd = useCallback(
(): void => setCount((count) => count + 1),
[]
);
const _rowRemove = useCallback(
(): void => setCount((count) => count - 1),
[]
);
return (
<Base
className={className}
isOuter
label={label}
withLabel={withLabel}
>
{!isDisabled && (
<div className='ui--Param-Vector-buttons'>
<Button
icon='plus'
label={t('Add item')}
onClick={_rowAdd}
/>
<Button
icon='minus'
isDisabled={values.length === 0}
label={t('Remove item')}
onClick={_rowRemove}
/>
</div>
)}
<Params
isDisabled={isDisabled}
onChange={setValues}
overrides={overrides}
params={params}
registry={registry}
values={values}
/>
</Base>
);
}
export default React.memo(Vector);
@@ -0,0 +1,75 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ParamDef, Props, RawParam } from '../types.js';
import React, { useEffect, useState } from 'react';
import { VecFixed } from '@pezkuwi/types';
import { isUndefined } from '@pezkuwi/util';
import Params from '../index.js';
import getInitValue from '../initValue.js';
import Base from './Base.js';
import useParamDefs from './useParamDefs.js';
import { getParams, getValues } from './Vector.js';
function getInitialValues (defaultValue: RawParam): RawParam[] {
return defaultValue.value instanceof VecFixed
? defaultValue.value.map((value) => ({ isValid: true, value: value as unknown }))
: getValues(defaultValue);
}
function VectorFixed ({ className = '', defaultValue, isDisabled = false, label, onChange, overrides, registry, type, withLabel }: Props): React.ReactElement<Props> | null {
const inputParams = useParamDefs(registry, type);
const [params] = useState<ParamDef[]>(() => getParams(inputParams, [], (inputParams[0].length || 1)));
const [values, setValues] = useState<RawParam[]>(() => getInitialValues(defaultValue));
// when !isDisable, generating an input list based on count
useEffect((): void => {
!isDisabled && inputParams.length &&
setValues((values): RawParam[] => {
const count = (inputParams[0].length || 1);
if (values.length === count) {
return values;
}
while (values.length < count) {
const value = getInitValue(registry, inputParams[0].type);
values.push({ isValid: !isUndefined(value), value });
}
return values.slice(0, count);
});
}, [inputParams, isDisabled, registry]);
// when our values has changed, alert upstream
useEffect((): void => {
onChange && onChange({
isValid: values.reduce((result: boolean, { isValid }) => result && isValid, true),
value: values.map(({ value }) => value)
});
}, [values, onChange]);
return (
<Base
className={className}
isOuter
label={label}
withLabel={withLabel}
>
<Params
isDisabled={isDisabled}
onChange={setValues}
overrides={overrides}
params={params}
registry={registry}
values={values}
/>
</Base>
);
}
export default React.memo(VectorFixed);
+101
View File
@@ -0,0 +1,101 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useCallback, useMemo, useRef } from 'react';
import { Dropdown } from '@pezkuwi/react-components';
import { GenericVote } from '@pezkuwi/types';
import { isBn, isNumber } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
import Bare from './Bare.js';
const AYE_MASK = 0b10000000;
// In this approach, it will avoid using additional local states and instead rely directly on the parent-provided values and methods.
function Vote ({ className = '', defaultValue: { value }, isDisabled, isError, onChange, withLabel }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
// Derive aye and conviction from the value prop
const { aye, conviction } = useMemo(() => {
// Logic sucks here, but to be honest, can not find other way to do it
const resolvedAye = value instanceof GenericVote
? value.isAye
: isBn(value)
? !!(value.toNumber() & AYE_MASK)
: isNumber(value)
? !!(value & AYE_MASK)
: typeof value === 'object' && value !== null && 'aye' in value
? !!value.aye
: true;
const resolvedConviction = value instanceof GenericVote
? value.conviction.index
: typeof value === 'object' && value !== null && 'conviction' in value
? Number(value.conviction)
: 0;
return {
aye: resolvedAye,
conviction: resolvedConviction
};
}, [value]);
const onChangeVote = useCallback(
(newAye: boolean) => {
onChange?.({ isValid: true, value: { aye: newAye, conviction } });
},
[conviction, onChange]
);
const onChangeConviction = useCallback(
(newConviction: number) => {
onChange?.({ isValid: true, value: { aye, conviction: newConviction } });
},
[aye, onChange]
);
const optAyeRef = useRef([
{ text: t('Nay'), value: false },
{ text: t('Aye'), value: true }
]);
const optConvRef = useRef([
{ text: t('None'), value: 0 },
{ text: t('Locked1x'), value: 1 },
{ text: t('Locked2x'), value: 2 },
{ text: t('Locked3x'), value: 3 },
{ text: t('Locked4x'), value: 4 },
{ text: t('Locked5x'), value: 5 },
{ text: t('Locked6x'), value: 6 }
]);
return (
<Bare className={className}>
<Dropdown
className='full'
defaultValue={aye}
isDisabled={isDisabled}
isError={isError}
label={t('aye: bool')}
onChange={onChangeVote}
options={optAyeRef.current}
withLabel={withLabel}
/>
<Dropdown
className='full'
defaultValue={conviction}
isDisabled={isDisabled}
isError={isError}
label={t('conviction: Conviction')}
onChange={onChangeConviction}
options={optConvRef.current}
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(Vote);
@@ -0,0 +1,62 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@pezkuwi/util';
import type { Props } from '../types.js';
import React, { useCallback, useMemo } from 'react';
import { Dropdown } from '@pezkuwi/react-components';
import { bnToBn, isFunction } from '@pezkuwi/util';
import Bare from './Bare.js';
type TextMap = Record<number, string>;
const options = [
{ text: 'Super majority approval', value: 0 },
{ text: 'Super majority rejection', value: 1 },
{ text: 'Simple majority', value: 2 }
];
export const textMap = options.reduce((textMap, { text, value }): TextMap => {
textMap[value] = text;
return textMap;
}, {} as unknown as TextMap);
function VoteThresholdParam ({ className = '', defaultValue: { value }, isDisabled, isError, label, onChange, withLabel }: Props): React.ReactElement<Props> {
const _onChange = useCallback(
(value: number) =>
onChange && onChange({
isValid: true,
value
}),
[onChange]
);
const defaultValue = useMemo(
// eslint-disable-next-line @typescript-eslint/unbound-method
() => isFunction((value as BN).toNumber)
? (value as BN).toNumber()
: bnToBn(value as number).toNumber(),
[value]
);
return (
<Bare className={className}>
<Dropdown
className='full'
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={_onChange}
options={options}
withLabel={withLabel}
/>
</Bare>
);
}
export default React.memo(VoteThresholdParam);
@@ -0,0 +1,203 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type React from 'react';
import type { Registry, TypeDef } from '@pezkuwi/types/types';
import type { ComponentMap, Props } from '../types.js';
import { getTypeDef } from '@pezkuwi/types';
import { TypeDefInfo } from '@pezkuwi/types/types';
import { isBn } from '@pezkuwi/util';
import Account from './Account.js';
import Amount from './Amount.js';
import Balance from './Balance.js';
import AccountId20 from './BasicAccountId20.js';
import AccountId32 from './BasicAccountId32.js';
import Bool from './Bool.js';
import BTreeMap from './BTreeMap.js';
import Bytes from './Bytes.js';
import Call from './Call.js';
import Cid from './Cid.js';
import Code from './Code.js';
import DispatchError from './DispatchError.js';
import DispatchResult from './DispatchResult.js';
import Enum from './Enum.js';
import Hash160 from './Hash160.js';
import Hash256 from './Hash256.js';
import Hash512 from './Hash512.js';
import KeyValue from './KeyValue.js';
import KeyValueArray from './KeyValueArray.js';
import Moment from './Moment.js';
import Null from './Null.js';
import OpaqueCall from './OpaqueCall.js';
import Option from './Option.js';
import Raw from './Raw.js';
import Struct from './Struct.js';
import Text from './Text.js';
import Tuple from './Tuple.js';
import Unknown from './Unknown.js';
import Vector from './Vector.js';
import VectorFixed from './VectorFixed.js';
import Vote from './Vote.js';
import VoteThreshold from './VoteThreshold.js';
interface TypeToComponent {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
c: React.ComponentType<any>;
t: string[];
}
const SPECIAL_TYPES = ['AccountId', 'AccountId20', 'AccountId32', 'AccountIndex', 'Address', 'Balance', 'BalanceOf', 'Vec<KeyValue>'];
const DISPATCH_ERROR = ['DispatchError', 'SpRuntimeDispatchError'];
const componentDef: TypeToComponent[] = [
{ c: Account, t: ['AccountId', 'Address', 'LookupSource', 'MultiAddress'] },
{ c: Amount, t: ['AccountIndex', 'i8', 'i16', 'i32', 'i64', 'i128', 'u8', 'u16', 'u32', 'u64', 'u128', 'u256'] },
{ c: Balance, t: ['Amount', 'Balance', 'BalanceOf'] },
{ c: Bool, t: ['bool'] },
{ c: Bytes, t: ['Bytes', 'Vec<u8>'] },
{ c: Call, t: ['Call', 'Proposal', 'RuntimeCall'] },
{ c: Cid, t: ['PalletAllianceCid'] },
{ c: Code, t: ['Code'] },
{ c: DispatchError, t: DISPATCH_ERROR },
{ c: DispatchResult, t: ['DispatchResult', 'Result<Null, SpRuntimeDispatchError>'] },
{ c: Raw, t: ['Raw', 'RuntimeSessionKeys', 'Keys'] },
{ c: Enum, t: ['Enum'] },
{ c: Hash256, t: ['Hash', 'H256'] },
{ c: Hash160, t: ['H160'] },
{ c: Hash512, t: ['H512'] },
{ c: KeyValue, t: ['KeyValue'] },
{ c: KeyValueArray, t: ['Vec<KeyValue>'] },
{ c: Moment, t: ['Moment', 'MomentOf'] },
{ c: Null, t: ['Null'] },
{ c: OpaqueCall, t: ['OpaqueCall'] },
{ c: Option, t: ['Option'] },
{ c: Text, t: ['String', 'Text'] },
{ c: Struct, t: ['Struct'] },
{ c: Tuple, t: ['Tuple'] },
{ c: BTreeMap, t: ['BTreeMap'] },
{ c: Vector, t: ['Vec', 'BTreeSet'] },
{ c: VectorFixed, t: ['VecFixed'] },
{ c: Vote, t: ['Vote'] },
{ c: VoteThreshold, t: ['VoteThreshold'] },
{ c: Unknown, t: ['Unknown'] }
];
const components: ComponentMap = componentDef.reduce((components, { c, t }): ComponentMap => {
t.forEach((type): void => {
components[type] = c;
});
return components;
}, {} as unknown as ComponentMap);
const warnList: string[] = [];
function fromDef ({ displayName, info, lookupName, sub, type }: TypeDef): string {
if (displayName && SPECIAL_TYPES.includes(displayName)) {
return displayName;
} else if (type.endsWith('RuntimeSessionKeys')) {
return 'RuntimeSessionKeys';
}
const typeValue = lookupName || type;
switch (info) {
case TypeDefInfo.Compact:
return (sub as TypeDef).type;
case TypeDefInfo.Option:
return 'Option';
case TypeDefInfo.Enum:
return 'Enum';
case TypeDefInfo.Result: {
const [, errSub] = (sub as TypeDef[]);
return DISPATCH_ERROR.includes(errSub.lookupName || errSub.type)
? 'DispatchResult'
: typeValue;
}
case TypeDefInfo.Struct:
return 'Struct';
case TypeDefInfo.BTreeSet:
return 'BTreeSet';
case TypeDefInfo.BTreeMap:
return 'BTreeMap';
case TypeDefInfo.Tuple:
return components[type] === Account
? type
: 'Tuple';
case TypeDefInfo.Vec:
return type === 'Vec<u8>'
? 'Bytes'
: ['Vec<KeyValue>'].includes(type)
? 'Vec<KeyValue>'
: 'Vec';
case TypeDefInfo.VecFixed:
return (sub as TypeDef).type === 'u8'
? type
: 'VecFixed';
default:
return typeValue;
}
}
export default function findComponent (registry: Registry, def: TypeDef, overrides: ComponentMap = {}): React.ComponentType<Props> {
// Explicit/special handling for Account20/32 types where they don't match
// the actual chain we are connected to
if (['AccountId20', 'AccountId32'].includes(def.type)) {
const defType = `AccountId${registry.createType('AccountId').length}`;
if (def.type !== defType) {
if (def.type === 'AccountId20') {
return AccountId20;
} else {
return AccountId32;
}
}
}
const findOne = (type?: string): React.ComponentType<Props> | null =>
type
? overrides[type] || components[type]
: null;
const type = fromDef(def);
let Component = findOne(def.lookupName) || findOne(def.type) || findOne(type);
if (!Component) {
try {
const instance = registry.createType(type as 'u32');
const raw = getTypeDef(instance.toRawType());
Component = findOne(raw.lookupName || raw.type) || findOne(fromDef(raw));
if (Component) {
return Component;
} else if (isBn(instance)) {
return Amount;
}
} catch (e) {
console.error(`params: findComponent: ${(e as Error).message}`);
}
// we only want to want once, not spam
if (!warnList.includes(type)) {
warnList.push(type);
console.info(`params: findComponent: No pre-defined component for type ${type} from ${TypeDefInfo[def.info]}: ${JSON.stringify(def)}`);
}
}
return Component || Unknown;
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Props } from '../types.js';
import React, { useMemo } from 'react';
import { getTypeDef } from '@pezkuwi/types';
import { encodeTypeDef } from '@pezkuwi/types/create';
import { isUndefined } from '@pezkuwi/util';
import findComponent from './findComponent.js';
import Static from './Static.js';
function formatJSON (input: string): string {
return input
.replace(/"/g, '')
.replace(/\\/g, '')
.replace(/:Null/g, '')
.replace(/:/g, ': ')
.replace(/,/g, ', ')
.replace(/^{_alias: {.*}, /, '{');
}
function Param ({ className = '', defaultValue, isDisabled, isError, isOptional, name, onChange, onEnter, onEscape, overrides, registry, type, withLength = true }: Props): React.ReactElement<Props> | null {
const Component = useMemo(
() => findComponent(registry, type, overrides),
[registry, type, overrides]
);
const label = useMemo(
(): string => {
const inner = encodeTypeDef(
registry,
// if our type is a Lookup, try and unwrap again
registry.isLookupType(type.lookupName || type.type)
? getTypeDef(registry.createType(type.type).toRawType())
: type
);
const fmtType = formatJSON(inner);
return `${isUndefined(name) ? '' : `${name}: `}${fmtType}${type.typeName && !fmtType.includes(type.typeName) ? ` (${type.typeName})` : ''}`;
},
[name, registry, type]
);
if (!Component) {
return null;
}
return isOptional
? (
<Static
defaultValue={defaultValue}
isOptional
label='None'
/>
)
: (
<Component
className={`${className} ui--Param`}
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
key={`${name || 'unknown'}:${label}`}
label={label}
name={name}
onChange={onChange}
onEnter={onEnter}
onEscape={onEscape}
overrides={overrides}
registry={registry}
type={type}
withLength={withLength}
/>
);
}
export default React.memo(Param);
@@ -0,0 +1,41 @@
// Copyright 2017-2025 @pezkuwi/react-params authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Registry, TypeDef } from '@pezkuwi/types/types';
import type { ParamDef } from '../types.js';
import { useMemo } from 'react';
import { createNamedHook } from '@pezkuwi/react-hooks';
import { getTypeDef } from '@pezkuwi/types/create';
function expandDef (registry: Registry, td: TypeDef): TypeDef {
try {
return getTypeDef(
registry.createType(td.type as 'u32').toRawType()
);
} catch {
return td;
}
}
function getDefs (registry: Registry, type: TypeDef): ParamDef[] {
const typeDef = expandDef(registry, type);
return typeDef.sub
? (Array.isArray(typeDef.sub) ? typeDef.sub : [typeDef.sub]).map((td): ParamDef => ({
length: typeDef.length,
name: td.name,
type: td
}))
: [];
}
function useParamDefsImpl (registry: Registry, type: TypeDef): ParamDef[] {
return useMemo(
() => getDefs(registry, type),
[registry, type]
);
}
export default createNamedHook('useParamDefs', useParamDefsImpl);