feat: redesign constants into card grid (#150)

* feat: redesign constants into card grid

* cleanup constants classnames
This commit is contained in:
Victor Oliva
2026-06-01 12:25:49 +02:00
committed by GitHub
parent 547c1e0129
commit 5cf060b363

View File

@@ -12,27 +12,30 @@ import {
} from "@/components/ui/tooltip"
import { withSubscribe } from "@/components/withSuspense"
import { pushWorkspaceEntry, setWorkspaceOpen } from "@/components/Workspace"
import { lookup$, runtimeCtx$ } from "@/state/chains/chain.state"
import { getTypeComplexity } from "@/utils/shape"
import type { RuntimeContext } from "@polkadot-api/observable-client"
import { CodecComponentType } from "@polkadot-api/react-builder"
import { state, useStateObservable, withDefault } from "@react-rxjs/core"
import { state, useStateObservable } from "@react-rxjs/core"
import { Dot, SquareEqual } from "lucide-react"
import { HexString } from "polkadot-api"
import { FC, useState } from "react"
import { firstValueFrom, map } from "rxjs"
import { twMerge } from "tailwind-merge"
import { v4 } from "uuid"
import { BlockPicker, selectedBlock$ } from "./Storage/BlockPicker"
import { ValueDisplay } from "./Storage/StorageSubscriptions"
const metadataConstants$ = state(
lookup$.pipe(
map((lookup) =>
lookup.metadata.pallets
selectedBlock$.pipe(
map(({ ctx }) =>
[...ctx.lookup.metadata.pallets]
.filter((p) => p.constants.length > 0)
.sort((a, b) => a.name.localeCompare(b.name))
.map((p) => ({
name: p.name,
constants: p.constants.sort((a, b) => a.name.localeCompare(b.name)),
constants: [...p.constants].sort((a, b) =>
a.name.localeCompare(b.name),
),
})),
),
),
@@ -43,8 +46,14 @@ export const Constants = withSubscribe(
const entries = useStateObservable(metadataConstants$)
return (
<div className="p-4 pb-0 flex flex-col gap-2 items-start leading-relaxed overflow-auto">
<ul>
<div className="p-4 space-y-2 overflow-auto @container">
<div className="w-full border-b border-border pb-3">
<label className="inline-flex flex-col gap-1 text-sm font-medium">
Block
<BlockPicker />
</label>
</div>
<ul className="grid w-full grid-cols-[repeat(auto-fill,minmax(min(100%,25rem),1fr))] gap-2">
{entries.map(({ name, constants }) => (
<PalletConstants key={name} pallet={name} entries={constants} />
))}
@@ -66,21 +75,13 @@ const PalletConstants: FC<{
docs: string[]
}>
}> = ({ pallet, entries }) => {
const [expanded, setExpanded] = useState(false)
return (
<li className="group">
<div
className="flex items-center gap-2 cursor-pointer"
onClick={() => setExpanded((e) => !e)}
>
<ExpandBtn expanded={expanded} />
<div>{pallet}</div>
<li className="min-w-0 rounded-lg border border-border bg-card text-card-foreground shadow-sm">
<div className="flex min-h-14 items-center gap-3 border-b border-border px-3">
<h2 className="truncate flex-1">{pallet}</h2>
<AddToWorkspace
className="h-7 w-7 opacity-0 group-hover:opacity-100"
onClick={async (evt) => {
evt.stopPropagation()
const ctx = await firstValueFrom(runtimeCtx$)
onClick={async () => {
const { ctx } = await firstValueFrom(selectedBlock$)
addPalletConstantsToWorkspace(
pallet,
@@ -97,13 +98,11 @@ const PalletConstants: FC<{
}}
/>
</div>
{expanded && (
<ul>
{entries.map((props) => (
<ConstantEntry key={props.name} {...props} />
))}
</ul>
)}
<ul className="divide-y divide-border">
{entries.map((props) => (
<ConstantEntry key={props.name} {...props} />
))}
</ul>
</li>
)
}
@@ -137,10 +136,11 @@ const ConstantsWorkspaceEntry: FC<{ context: ConstantsWorkspaceContext }> = ({
}
const constantValueProps$ = state(
runtimeCtx$.pipe(
map(({ dynamicBuilder, lookup }) => ({
builder: dynamicBuilder,
lookup,
selectedBlock$.pipe(
map(({ ctx }) => ({
ctx,
builder: ctx.dynamicBuilder,
lookup: ctx.lookup,
})),
),
null,
@@ -162,15 +162,22 @@ const ConstantEntry: FC<{
const titleElement = (
<div
className={twMerge(
"flex items-center gap-2",
"flex min-w-0 items-center gap-2",
!isInline && "cursor-pointer",
)}
onClick={() => setExpanded((e) => !e)}
>
{isInline ? <Dot size={16} /> : <ExpandBtn expanded={expanded} />}
<span className="shrink-0">
{isInline ? <Dot size={16} /> : <ExpandBtn expanded={expanded} />}
</span>
<Tooltip disableHoverableContent>
<TooltipTrigger className="cursor-default">
<div className={isInline ? "text-foreground/60" : ""}>
<TooltipTrigger className="min-w-0 cursor-default shrink-0">
<div
className={twMerge(
"truncate text-left text-sm",
isInline ? "text-foreground/60" : "font-medium",
)}
>
{name + (isInline ? ":" : "")}
</div>
</TooltipTrigger>
@@ -181,23 +188,26 @@ const ConstantEntry: FC<{
) : null}
</Tooltip>
{isInline ? (
<ViewCodec
codecType={type}
value={{
type: CodecComponentType.Initial,
value: value,
}}
metadata={props.lookup.metadata}
/>
<div className="min-w-0 text-sm">
<ViewCodec
codecType={type}
value={{
type: CodecComponentType.Initial,
value: value,
}}
metadata={props.lookup.metadata}
/>
</div>
) : null}
</div>
)
return (
<li className="pl-4">
<li className="min-w-0 px-3 py-2">
{titleElement}
{!isInline && expanded && (
<ConstantValue
ctx={props.ctx}
type={type}
decoded={props.builder.buildDefinition(type).dec(value)}
/>
@@ -206,17 +216,15 @@ const ConstantEntry: FC<{
)
}
const defaultedCtx$ = runtimeCtx$.pipeState(withDefault(null))
const ConstantValue: FC<{ type: number; decoded: unknown }> = ({
type,
decoded,
}) => {
const ConstantValue: FC<{
ctx: Pick<RuntimeContext, "lookup" | "dynamicBuilder">
type: number
decoded: unknown
}> = ({ ctx, type, decoded }) => {
const [mode, setMode] = useState<"json" | "decoded">("decoded")
const ctx = useStateObservable(defaultedCtx$)
if (!ctx) return null
return (
<div className="pl-6 py-2 flex flex-col gap-2 items-start overflow-hidden w-full">
<div className="flex w-full flex-col items-start gap-2 overflow-hidden py-2 pl-6">
<ButtonGroup
value={mode}
onValueChange={setMode as any}
@@ -231,7 +239,7 @@ const ConstantValue: FC<{ type: number; decoded: unknown }> = ({
},
]}
/>
<div className="overflow-auto w-full">
<div className="w-full overflow-auto">
<ValueDisplay
mode={mode}
ctx={ctx}