zzzUI

Reward track

React 等级奖励列表组件,即赛季通行证或战令页面:每个等级一行,奖励放在格子里,状态分为已领取、可领取与未解锁。

安装

terminal
# once per project: where @zzzui items come frompnpm dlx shadcn@latest registry add @zzzui=https://zzzui.catforest.dev/r/{name}.jsonpnpm dlx shadcn@latest add @zzzui/reward-track

示例

待领取的等级奖励

  1. Level 2, claimed.
    Claimed
  2. Level 3, ready to claim.
  3. Level 4, ready to claim, your level.
  4. Level 5, locked.
    Reach level 5
  5. Level 6, locked.
    Reach level 6
reward-track-levels.tsx
import { useState } from "react";import { Button } from "@/components/zero/button";import { CellIcon } from "@/components/zero/icons/cell";import { ChipIcon } from "@/components/zero/icons/chip";import { CrateIcon } from "@/components/zero/icons/crate";import { KeyIcon } from "@/components/zero/icons/key";import { KitIcon } from "@/components/zero/icons/kit";import { InventorySlot } from "@/components/zero/inventory";import { RewardTier, RewardTrack } from "@/components/zero/reward-track";const current = 4;const levels = [  {    level: 2,    rewards: [      { name: "Power cell", icon: CellIcon, rarity: "common", count: 2 },    ],  },  {    level: 3,    rewards: [      { name: "Repair kit", icon: KitIcon, rarity: "uncommon", count: 1 },      { name: "Power cell", icon: CellIcon, rarity: "common", count: 3 },    ],  },  {    level: 4,    rewards: [      { name: "Sealed crate", icon: CrateIcon, rarity: "rare", count: 1 },    ],  },  {    level: 5,    rewards: [      { name: "Route chip", icon: ChipIcon, rarity: "epic", count: 1 },      { name: "Repair kit", icon: KitIcon, rarity: "uncommon", count: 2 },    ],  },  {    level: 6,    rewards: [      { name: "Gate key", icon: KeyIcon, rarity: "legendary", count: 1 },    ],  },] as const;export default function RewardTrackLevels() {  const [claimed, setClaimed] = useState<number[]>([2]);  const state = (level: number) =>    level > current ? "locked" : claimed.includes(level) ? "claimed" : "ready";  const ready = levels.filter((l) => state(l.level) === "ready");  return (    <div className="grid w-full max-w-2xl gap-3">      <div className="flex justify-end">        <Button          size="sm"          variant="secondary"          disabled={ready.length === 0}          onClick={() =>            setClaimed((c) => [...c, ...ready.map((l) => l.level)])          }        >          Claim all        </Button>      </div>      <RewardTrack aria-label="Courier levels">        {levels.map((l) => (          <RewardTier            key={l.level}            level={l.level}            state={state(l.level)}            current={l.level === current}            onClaim={() => setClaimed((c) => [...c, l.level])}          >            {l.rewards.map((r) => (              <InventorySlot                key={r.name}                rarity={r.rarity}                count={r.count}                aria-label={`${r.name}, ${r.count}`}              >                <r.icon />              </InventorySlot>            ))}          </RewardTier>        ))}      </RewardTrack>    </div>  );}

行为

每个等级一行:大号斜体的等级数字放在斜切的槽中,奖励(InventorySlots)在中间,行末是可执行的操作。ready 点亮等级数字并加上强调色边缘,显示 Claim 按钮;claimed 将奖励变暗,并以对勾标明已领取;locked 将奖励进一步变暗,并说明解锁条件。current 标记当前所在的等级。

每行开头会以文字向读屏软件说明等级与状态(「Level 5, ready to claim」)。「Claim all」按钮应放在外层页面:只需在外层状态中做一次批量更新。

属性

RewardTier

属性类型默认值说明
levelnumber—等级。
state"claimed" | "ready" | "locked"—领取状态。
currentboolean—是否为当前所在等级。
onClaim() => void—按下 Claim 时调用。
noteReactNode—关于该等级本身的一行说明,不随状态变暗。
lockedReactNode"Reach level n"未解锁时显示的解锁条件。
labelsPartial<RewardTierLabels>rewardTierLabels换成其他语言的文字:读屏前缀、「Claim」「Claimed」,以及未解锁时的条件。

RewardTrack