@boost33/ui 0.7.1 → 0.8.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +257 -0
  2. package/dist/buttons.module.css.js +10 -0
  3. package/dist/buttons.module.css.js.map +1 -0
  4. package/dist/components/AppShell/AppShell.d.ts +129 -17
  5. package/dist/components/AppShell/AppShell.js +78 -50
  6. package/dist/components/AppShell/AppShell.js.map +1 -1
  7. package/dist/components/AppShell/AppShell.module.css.js +22 -14
  8. package/dist/components/AppShell/AppShell.module.css.js.map +1 -1
  9. package/dist/components/AppSwitcher/AppSwitcher.d.ts +92 -45
  10. package/dist/components/AppSwitcher/AppSwitcher.js +76 -68
  11. package/dist/components/AppSwitcher/AppSwitcher.js.map +1 -1
  12. package/dist/components/AppSwitcher/AppSwitcher.module.css.js +42 -36
  13. package/dist/components/AppSwitcher/AppSwitcher.module.css.js.map +1 -1
  14. package/dist/components/AppSwitcher/icons.js +10 -10
  15. package/dist/components/AppSwitcher/icons.js.map +1 -1
  16. package/dist/components/AppSwitcher/labels.d.ts +15 -23
  17. package/dist/components/AppSwitcher/labels.js +3 -4
  18. package/dist/components/AppSwitcher/labels.js.map +1 -1
  19. package/dist/components/CommandPalette/CommandPalette.d.ts +8 -2
  20. package/dist/components/CommandPalette/CommandPalette.js.map +1 -1
  21. package/dist/components/NavItem/NavItem.d.ts +15 -2
  22. package/dist/components/NavItem/NavItem.js +14 -11
  23. package/dist/components/NavItem/NavItem.js.map +1 -1
  24. package/dist/components/NavItem/NavItem.module.css.js +6 -2
  25. package/dist/components/NavItem/NavItem.module.css.js.map +1 -1
  26. package/dist/components/NavSection/NavSection.module.css.js +5 -5
  27. package/dist/components/NotificationButton/NotificationButton.d.ts +29 -0
  28. package/dist/components/NotificationButton/NotificationButton.js +23 -0
  29. package/dist/components/NotificationButton/NotificationButton.js.map +1 -0
  30. package/dist/components/NotificationButton/NotificationButton.module.css.js +14 -0
  31. package/dist/components/NotificationButton/NotificationButton.module.css.js.map +1 -0
  32. package/dist/components/NotificationButton/labels.d.ts +12 -0
  33. package/dist/components/NotificationButton/labels.js +15 -0
  34. package/dist/components/NotificationButton/labels.js.map +1 -0
  35. package/dist/components/NotificationCenter/NotificationCenter.js +37 -36
  36. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  37. package/dist/components/NotificationCenter/NotificationCenter.module.css.js +43 -43
  38. package/dist/components/Sidebar/NavHeightBudgetWarning.js +10 -10
  39. package/dist/components/Sidebar/NavHeightBudgetWarning.js.map +1 -1
  40. package/dist/components/Sidebar/Sidebar.module.css.js +1 -1
  41. package/dist/components/TopBar/TopBar.d.ts +55 -4
  42. package/dist/components/TopBar/TopBar.js +24 -7
  43. package/dist/components/TopBar/TopBar.js.map +1 -1
  44. package/dist/components/TopBar/TopBar.module.css.js +12 -8
  45. package/dist/components/TopBar/TopBar.module.css.js.map +1 -1
  46. package/dist/components/TopBar/labels.d.ts +20 -0
  47. package/dist/components/TopBar/labels.js +15 -0
  48. package/dist/components/TopBar/labels.js.map +1 -0
  49. package/dist/components/WalletChip/WalletChip.d.ts +77 -2
  50. package/dist/components/WalletChip/WalletChip.js +142 -50
  51. package/dist/components/WalletChip/WalletChip.js.map +1 -1
  52. package/dist/components/WalletChip/WalletChip.module.css.js +93 -47
  53. package/dist/components/WalletChip/WalletChip.module.css.js.map +1 -1
  54. package/dist/components/WalletChip/labels.d.ts +63 -1
  55. package/dist/components/WalletChip/labels.js +21 -6
  56. package/dist/components/WalletChip/labels.js.map +1 -1
  57. package/dist/deprecated-prop-warning.d.ts +5 -0
  58. package/dist/deprecated-prop-warning.js +11 -0
  59. package/dist/deprecated-prop-warning.js.map +1 -0
  60. package/dist/index.d.ts +9 -3
  61. package/dist/index.js +55 -49
  62. package/dist/index.js.map +1 -1
  63. package/dist/nav-height-budget.d.ts +12 -9
  64. package/dist/nav-height-budget.js +3 -3
  65. package/dist/nav-height-budget.js.map +1 -1
  66. package/dist/style.css +1 -1
  67. package/dist/theme.css +1 -1
  68. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"WalletChip.js","sources":["../../../src/components/WalletChip/WalletChip.tsx"],"sourcesContent":["'use client'\n\n// A client module: the chip wires `onTopUp` to a button's onClick, which a\n// Server Component cannot carry. The rest of the shell stays on the server --\n// see Sidebar and B33-846.\n\nimport styles from './WalletChip.module.css'\nimport { DEFAULT_WALLET_LOCALE, formatTokens, thousandsFigure } from './format'\nimport { RetryMark, WalletMark } from './icons'\nimport { resolveWalletChipLabels, type WalletChipLabels } from './labels'\n\n// Past this fraction of the cycle's grant burned, the chip stops resting. The\n// spec's threshold, in one place so the derivation and the styling agree.\n//\n// It stays in the package and is not a prop, which is the whole reason\n// `burnedFraction` is a fraction rather than a `threshold?: boolean` (B33-483).\n// The consumer measures its own burn; 80% is where four products agree that\n// measurement means something, and a boolean would hand each of them the policy\n// and let it drift. It also happens to agree with the middle rung of Score's own\n// burndown alerts, so adopting it changed no product decision.\nconst THRESHOLD_BURN = 0.8\n\n// The floor under a non-zero bar segment, named once so the width expression\n// below reads as what it is. The value and its derivation live in the token --\n// see --spacing-wallet-segment-min in tokens.css.\nconst SEGMENT_MIN = 'var(--spacing-wallet-segment-min)'\n\n/**\n * A bar segment's CSS `width`: its honest share of everything the cycle started\n * with, floored so that a non-zero amount is never drawn below one visible mark.\n *\n * `max()` rather than a `min-width` on the segment classes, and the reason is the\n * zero case (B33-590). A stylesheet sees only the percentage, and no CSS length\n * can tell 0% from 0.3% -- a bare `min-width` would floor a wallet that holds\n * none of that kind of token into a phantom sliver, which is the same lie in the\n * other direction. The decision needs the amount itself, so it is made here where\n * the amount is, in one expression instead of a rule plus a signal from the\n * component to switch it off. `max()` also keeps the honest percentage wherever\n * the share clears the floor: the floor only ever wins when the truth is\n * unpaintable.\n *\n * Why it needs flooring at all: once a seller has bought tokens the grant is a\n * small share of the cycle, and a small share of a 240px bar is a fraction of a\n * pixel. Score measured 3,000 granted rendering at `width: 0.3%` -- 0.72px, which\n * paints nothing, while the ledger still held the grant. Both segments are\n * floored: purchased burns second, so it drains to zero through every small\n * number on the way, and 10 purchased on a spent 3,000 grant is 0.33% of the\n * cycle and the seller's entire remaining balance.\n *\n * This is the second time the rendering has been reported. Score fixed it in its\n * own wallet markup; B33-482 replaced that markup with this component, which\n * carried no floor. Its record of the first fix is still on disk there, as dead\n * code, at components/app-shell/wallet-bar.ts.\n */\nfunction segmentWidth(amount: number, cycleTotal: number): string {\n // A cycle of nothing has no shares in it. Guarded because the divisor is\n // reachable -- a known balance of zero on a cycle that granted zero rendered\n // `width: NaN%`, which a browser drops rather than reports.\n const share = cycleTotal > 0 ? (amount / cycleTotal) * 100 : 0\n if (amount <= 0) return `${share}%`\n return `max(${share}%, ${SEGMENT_MIN})`\n}\n\n/**\n * The props that say nothing about a balance, so both states take them\n * identically: where the chip is drawn, what it is wired to, and the words and\n * number formats it renders with.\n */\ninterface WalletChipChromeProps {\n /** The 56px rail. Abbreviates the balance and turns the split on its side. */\n collapsed?: boolean\n /**\n * Top up. Wiring for the one button the sidebar is allowed to have.\n *\n * **It is also what makes that button exist.** Omit it and the chip draws no\n * top-up affordance at all, in every state, and that is the fix B33-1270 made.\n * The render used to be gated on `!unknown` alone, so a consumer that passed\n * no handler got a visible control that did nothing and could not be removed\n * -- `.topUp`'s class name is CSS-Modules-hashed, so there is no selector to\n * reach it from outside the package.\n *\n * That was not hypothetical in either consumer. The Wallet passes no handler\n * and ships the dead button today\n * (`boostledger/src/app/(wallet)/WalletShell.tsx`), and Score wrote a whole\n * client module to arm it across a Server Component boundary rather than live\n * with it (`boostscore/components/app-shell/wallet-chip-top-up.tsx`, which\n * says so at length).\n *\n * Nothing renders in the button's place. It carries no information of its own\n * -- only its own affordance -- so a consumer with nowhere to send someone is\n * better served by the chip without it than by a disabled one they did not\n * ask for (ADR-0010, \"Not entitled is not disabled\", on why unavailable is not\n * the same as disabled). B33-1047 is the other half: an `href` form of the\n * same affordance, for a consumer that cannot pass a callback at all.\n */\n onTopUp?: () => void\n /**\n * The chip's user-facing strings. Every field has an English default, so a\n * consumer supplies only the ones it translates; the first two consumers are\n * French-default. The package takes no i18n dependency -- it renders what it\n * is given (B33-849).\n *\n * Every field is a plain string, so the whole object crosses the React Server\n * Component boundary and a Next App Router page can build it. The four labels\n * that carry a value mark its position with a `%placeholder%` (B33-1277). None\n * of them takes a function: that form only ever worked from a Client Component\n * and nothing in the type said so (B33-880, B33-1278).\n */\n labels?: Partial<WalletChipLabels>\n /**\n * The BCP-47 language tag the chip's figures are formatted in. Defaults to\n * `en-US`, which is what the chip rendered before this prop existed.\n *\n * `labels` gives the consumer the chip's words; this gives it their numbers.\n * French writes `4 600` where English writes `4,600`, and a label cannot fix\n * it -- the label functions receive the figures already formatted, so before\n * this prop there was no hook at all (B33-876). Grouping and the decimal\n * separator are CLDR facts about a locale rather than a property of the\n * consumer's sentence, so a tag is enough: `Intl.NumberFormat` knows the rest,\n * and a tag is a plain string, which a Server Component can pass where a\n * formatter function could not.\n *\n * It reaches the visible figures and, because the same strings are\n * interpolated into them, the `bar` and `railBalance` accessible names.\n *\n * The `k` after the rail's abbreviated balance is a word, not a number, so it\n * is `labels.railFigure` and not this.\n */\n locale?: string\n}\n\n/**\n * The chip with a balance the consumer stands behind. The two remaining figures\n * are required, because this is the state that renders them.\n */\nexport interface WalletChipKnownProps extends WalletChipChromeProps {\n /**\n * Absent or `false`. The discriminant: it is what makes the figures below\n * required here and forbidden in {@link WalletChipUnknownProps}.\n */\n unknown?: false\n /** Remaining granted tokens. Granted burns first and expires. */\n granted: number\n /** Remaining purchased tokens. Purchased never expires. */\n purchased: number\n /**\n * What this cycle's grant started with, if the consumer knows. The bar's full\n * width alongside purchased, so the part already burned draws as empty track\n * and the filled portion shortens as the month runs.\n *\n * **Optional, and that is the fix B33-483 made.** It used to be required, and\n * no consumer could satisfy it honestly: it is the Ledger's\n * `grant_tokens_per_cycle`, and it cannot be reconstructed from a balance --\n * `cycleUsedPct` counts purchased-bucket spend, is not `(cycle total -\n * remaining)` once a credit, refund or void has touched the account, and is not\n * bounded at 100. So the Wallet passed `grantCycle = granted`, which pins\n * `burned` at 0 and held the threshold state unreachable in the reference\n * implementation, and Score passed a local per-plan belief instead.\n *\n * Omitted, the bar measures against `granted + purchased` -- the balance\n * itself. It then draws full and claims nothing about what burned, which is the\n * honest rendering when nobody can say.\n *\n * It no longer decides the threshold on its own: {@link burnedFraction} does\n * that, and this is only consulted when that is absent.\n */\n grantCycle?: number\n /**\n * How much of the cycle's grant has burned, as a fraction -- `0.88` for 88%.\n * Drives the threshold state, and is the figure the chip rounds into its pill.\n *\n * Passed in rather than derived, for exactly the reason {@link burnNote}\n * already is: the burn is the product's measurement to make, not the chip's.\n * The chip kept the arithmetic while taking the runway line as a prop, and fed\n * it from a denominator no product could supply -- so the measurement was the\n * chip's in name and nobody's in fact (B33-483).\n *\n * The *policy* stays here. `THRESHOLD_BURN` is 0.8 in this package, so four\n * products trip at the same place and cannot drift to four different numbers.\n * That is why this is a fraction and not a `threshold?: boolean`: a boolean\n * would move the 80% decision into each consumer, and it would leave the pill\n * with no figure to print.\n *\n * Supplied, it wins. Absent, the chip derives it from {@link grantCycle} as it\n * always did; absent with no `grantCycle` either, nothing is claimed and the\n * chip rests.\n *\n * It is not clamped. A consumer that passes a percentage where a fraction\n * belongs renders a visibly absurd pill rather than a plausible \"100%\", and a\n * wrong figure that looks wrong is the one that gets fixed.\n */\n burnedFraction?: number\n /** When the current grant expires, preformatted for display, e.g. \"30 Sep\". */\n grantExpires?: string\n /**\n * The threshold state's runway line, preformatted, e.g. \"About four days left\n * at your current burn.\" Passed in rather than derived: the burn estimate is\n * the product's to make, not the chip's -- the chip renders what it is given.\n */\n burnNote?: string\n}\n\n/**\n * The chip whose balance could not be confirmed. It renders an em dash, no bar\n * and no button, so **it takes no figures at all** -- each one below is\n * `?: never`.\n *\n * That is deliberate and it is the second half of B33-483. The figures were\n * required in every state, so both consumers manufactured zeroes to get past the\n * type and then apologised for them in a comment -- \"these zeroes are never\n * rendered ... but they are not a balance and must not be read as one\"\n * (`boostscore/components/app-shell/wallet-chip.tsx`), and the same again in\n * `wallet-slot.tsx` and `boostledger/src/app/(wallet)/layout.tsx`. A zero is a\n * confident claim about someone's money. The type now refuses it rather than\n * asking a comment to.\n *\n * `undefined` is still accepted, so a consumer that must build one props object\n * can write `{ unknown: true, granted: undefined }` instead of branching.\n */\nexport interface WalletChipUnknownProps extends WalletChipChromeProps {\n /** The balance could not be confirmed. Renders as unknown, never as zero. */\n unknown: true\n granted?: never\n purchased?: never\n grantCycle?: never\n burnedFraction?: never\n /** Not rendered in this state: the footer only draws on a resting chip. */\n grantExpires?: never\n /** Not rendered in this state: the runway line only draws past the threshold. */\n burnNote?: never\n}\n\n/**\n * A discriminated union on `unknown`, and the discriminant has consequences for\n * how a consumer *types* its own code, not only what it passes:\n *\n * - A helper that returns props for both states needs an explicit\n * `: WalletChipProps` return annotation. Without one, TypeScript widens the\n * `unknown: true` in an object literal to `boolean`, which matches neither\n * member, and the spread at the call site stops compiling.\n * - `Omit<WalletChipProps, 'onTopUp'>` is not what it looks like. `Omit` over a\n * union flattens it into one object and loses the discrimination. Omit from\n * the members instead -- `Omit<WalletChipKnownProps, 'onTopUp'> |\n * Omit<WalletChipUnknownProps, 'onTopUp'>` -- which is why both are exported.\n */\nexport type WalletChipProps = WalletChipKnownProps | WalletChipUnknownProps\n\n/**\n * The granted/purchased bar. Granted is drawn first, because granted burns\n * first. The accessible label is passed in whole rather than assembled here:\n * word order is the consumer's, not English's (B33-849).\n */\nfunction Bar({ granted, purchased, cycleTotal, tone, label }: { granted: number; purchased: number; cycleTotal: number; tone: 'rest' | 'threshold'; label: string }) {\n return (\n <div className={styles.bar} data-tone={tone} role=\"img\" aria-label={label}>\n <span className={styles.segmentGranted} data-testid=\"segment-granted\" style={{ width: segmentWidth(granted, cycleTotal) }} />\n <span className={styles.segmentPurchased} data-testid=\"segment-purchased\" style={{ width: segmentWidth(purchased, cycleTotal) }} />\n </div>\n )\n}\n\nexport function WalletChip({ granted = 0, purchased = 0, grantCycle, burnedFraction, grantExpires, burnNote, unknown = false, collapsed = false, onTopUp, labels, locale = DEFAULT_WALLET_LOCALE }: WalletChipProps) {\n const t = resolveWalletChipLabels(labels)\n // Assembled lazily: the unknown state draws no bar, and a consumer's `bar`\n // implementation can throw on the zeroes the destructure above defaults the\n // degraded path's absent figures to. Calling it only where a bar renders keeps\n // that crash surface off the render that exists precisely because fetching the\n // balance already failed (B33-875). B33-483 stopped the consumer having to pass\n // those zeroes; the chip still has to pick something, so the laziness stays.\n const buildBarLabel = () =>\n t.bar(formatTokens(granted, locale), formatTokens(purchased, locale), formatTokens(granted + purchased, locale))\n const balance = granted + purchased\n // Everything the cycle started with: the grant plus the purchased balance\n // sitting alongside it. Measuring against this is what makes the bar shorten\n // as tokens burn instead of only re-proportioning to a full width.\n //\n // With no `grantCycle` the denominator falls back to `granted + purchased`, so\n // the bar fills and there is no burned track -- the honest drawing when nobody\n // can say what the cycle started with (B33-483). The zero-division guard inside\n // segmentWidth still covers the case where that balance is itself zero.\n const cycleTotal = (grantCycle ?? granted) + purchased\n // How much of the cycle's grant has burned. The consumer's own figure wins;\n // `grantCycle` derives it exactly as it always did; with neither, nothing is\n // claimed and the chip rests. `grantCycle` is tested against undefined rather\n // than leaned on for falsiness, because 0 and \"not supplied\" both resolve to a\n // burn of 0 and must keep doing so.\n const derivedBurn = grantCycle !== undefined && grantCycle > 0 ? (grantCycle - granted) / grantCycle : 0\n const burned = burnedFraction ?? derivedBurn\n const isThreshold = !unknown && burned >= THRESHOLD_BURN\n\n if (collapsed) {\n // The abbreviated balance, assembled once: it is both the rail's visible\n // text and the figure the group's accessible name is built around, and a\n // sighted user and a screen-reader user must not be told different numbers.\n // Built only when a balance is known, for the same reason buildBarLabel is\n // lazy -- the unknown rail shows no figure, and a consumer's railFigure\n // should not run on a degraded path it never renders on (B33-875).\n const railBalance = unknown ? undefined : t.railFigure(thousandsFigure(balance, locale))\n return (\n <div className={styles.rail} role=\"group\" aria-label={railBalance === undefined ? t.railBalanceUnknown : t.railBalance(railBalance)} tabIndex={0}>\n <WalletMark className={styles.railIcon} />\n <span className={styles.railFigure}>{railBalance ?? '—'}</span>\n {unknown ? <span className={styles.srOnly}>{t.railUnknown}</span> : <Bar granted={granted} purchased={purchased} cycleTotal={cycleTotal} tone={isThreshold ? 'threshold' : 'rest'} label={buildBarLabel()} />}\n </div>\n )\n }\n\n return (\n <div className={styles.chip} role=\"group\" aria-label={t.region} tabIndex={0} data-threshold={isThreshold ? 'true' : undefined} data-unknown={unknown ? 'true' : undefined}>\n <div className={styles.header}>\n <span className={styles.name}>{t.name}</span>\n {isThreshold && <span className={styles.pill}>{Math.round(burned * 100)}%</span>}\n </div>\n\n {unknown ? (\n <div className={styles.figure}>\n <span className={styles.amount} aria-hidden=\"true\">\n {'—'}\n </span>\n <RetryMark className={styles.retry} />\n <span className={styles.srOnly}>{t.balanceUnknown}</span>\n </div>\n ) : (\n <div className={styles.figure}>\n <span className={styles.amount}>{formatTokens(balance, locale)}</span>\n <span className={styles.unit}>{t.unit}</span>\n </div>\n )}\n\n {unknown ? (\n <div className={styles.bar} data-tone=\"unknown\" aria-hidden=\"true\" />\n ) : (\n <Bar granted={granted} purchased={purchased} cycleTotal={cycleTotal} tone={isThreshold ? 'threshold' : 'rest'} label={buildBarLabel()} />\n )}\n\n {!unknown && !isThreshold && (\n <div className={styles.details}>\n <dl className={styles.split}>\n <div className={styles.splitRow}>\n <dt className={styles.splitLabel}>\n <span className={styles.dotGranted} aria-hidden=\"true\" />\n {t.granted}\n </dt>\n <dd className={styles.splitAmount}>{formatTokens(granted, locale)}</dd>\n </div>\n <div className={styles.splitRow}>\n <dt className={styles.splitLabel}>\n <span className={styles.dotPurchased} aria-hidden=\"true\" />\n {t.purchased}\n </dt>\n <dd className={styles.splitAmount}>{formatTokens(purchased, locale)}</dd>\n </div>\n </dl>\n {grantExpires && <p className={styles.footer}>{t.grantExpiry(grantExpires)}</p>}\n </div>\n )}\n\n {isThreshold && burnNote && <p className={styles.burnNote}>{burnNote}</p>}\n\n {/* A handler is what makes the button exist, not only what it does when\n pressed (B33-1270). `!unknown` alone drew it for a consumer with\n nowhere to send anyone, and a hashed class name left them no way to\n take it back out -- see `onTopUp`. Tested against `undefined` rather\n than for truthiness so that `onTopUp={undefined}`, which is what a\n consumer's own absent value looks like, reads the same as omitting it. */}\n {!unknown && onTopUp !== undefined && (\n <button type=\"button\" className={styles.topUp} data-grown={isThreshold ? 'true' : undefined} onClick={onTopUp}>\n {t.topUp}\n </button>\n )}\n </div>\n )\n}\n"],"names":["THRESHOLD_BURN","SEGMENT_MIN","segmentWidth","amount","cycleTotal","share","Bar","granted","purchased","tone","label","jsxs","styles","jsx","WalletChip","grantCycle","burnedFraction","grantExpires","burnNote","unknown","collapsed","onTopUp","labels","locale","DEFAULT_WALLET_LOCALE","t","resolveWalletChipLabels","buildBarLabel","formatTokens","balance","derivedBurn","burned","isThreshold","railBalance","thousandsFigure","WalletMark","RetryMark"],"mappings":"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"}
1
+ {"version":3,"file":"WalletChip.js","sources":["../../../src/components/WalletChip/WalletChip.tsx"],"sourcesContent":["'use client'\n\n// A client module: the chip wires `onTopUp` to a button's onClick, which a\n// Server Component cannot carry. The rest of the shell stays on the server --\n// see Sidebar and B33-846.\n\nimport type { ElementType } from 'react'\nimport { useAnchoredOverlay } from '../../anchored-overlay'\nimport { Chevron } from '../AppSwitcher/icons'\nimport buttons from '../../buttons.module.css'\nimport styles from './WalletChip.module.css'\nimport { DEFAULT_WALLET_LOCALE, formatTokens, thousandsFigure } from './format'\nimport { RetryMark, WalletMark } from './icons'\nimport { resolveWalletChipLabels, type WalletChipLabels } from './labels'\n\n// Past this fraction of the cycle's grant burned, the chip stops resting. The\n// spec's threshold, in one place so the derivation and the styling agree.\n//\n// It stays in the package and is not a prop, which is the whole reason\n// `burnedFraction` is a fraction rather than a `threshold?: boolean` (B33-483).\n// The consumer measures its own burn; 80% is where four products agree that\n// measurement means something, and a boolean would hand each of them the policy\n// and let it drift. It also happens to agree with the middle rung of Score's own\n// burndown alerts, so adopting it changed no product decision.\nconst THRESHOLD_BURN = 0.8\n\n// The floor under a non-zero bar segment, named once so the width expression\n// below reads as what it is. The value and its derivation live in the token --\n// see --spacing-wallet-segment-min in tokens.css.\nconst SEGMENT_MIN = 'var(--spacing-wallet-segment-min)'\n\n/**\n * A bar segment's CSS `width`: its honest share of everything the cycle started\n * with, floored so that a non-zero amount is never drawn below one visible mark.\n *\n * `max()` rather than a `min-width` on the segment classes, and the reason is the\n * zero case (B33-590). A stylesheet sees only the percentage, and no CSS length\n * can tell 0% from 0.3% -- a bare `min-width` would floor a wallet that holds\n * none of that kind of token into a phantom sliver, which is the same lie in the\n * other direction. The decision needs the amount itself, so it is made here where\n * the amount is, in one expression instead of a rule plus a signal from the\n * component to switch it off. `max()` also keeps the honest percentage wherever\n * the share clears the floor: the floor only ever wins when the truth is\n * unpaintable.\n *\n * Why it needs flooring at all: once a seller has bought tokens the grant is a\n * small share of the cycle, and a small share of a 240px bar is a fraction of a\n * pixel. Score measured 3,000 granted rendering at `width: 0.3%` -- 0.72px, which\n * paints nothing, while the ledger still held the grant. Both segments are\n * floored: purchased burns second, so it drains to zero through every small\n * number on the way, and 10 purchased on a spent 3,000 grant is 0.33% of the\n * cycle and the seller's entire remaining balance.\n *\n * This is the second time the rendering has been reported. Score fixed it in its\n * own wallet markup; B33-482 replaced that markup with this component, which\n * carried no floor. Its record of the first fix is still on disk there, as dead\n * code, at components/app-shell/wallet-bar.ts.\n */\nfunction segmentWidth(amount: number, cycleTotal: number): string {\n // A cycle of nothing has no shares in it. Guarded because the divisor is\n // reachable -- a known balance of zero on a cycle that granted zero rendered\n // `width: NaN%`, which a browser drops rather than reports.\n const share = cycleTotal > 0 ? (amount / cycleTotal) * 100 : 0\n if (amount <= 0) return `${share}%`\n return `max(${share}%, ${SEGMENT_MIN})`\n}\n\n/**\n * The props that say nothing about a balance, so both states take them\n * identically: where the chip is drawn, what it is wired to, and the words and\n * number formats it renders with.\n */\ninterface WalletChipChromeProps {\n /**\n * Where the chip is drawn. `'sidebar'`, the default, is the card it has always\n * been, pinned at the sidebar's foot. `'topbar'` is the 3 Nov 2026 redesign's\n * token chip (B33-1712, boards 20 and 21): a 32px pill in the top bar, drawn\n * like the app switcher, that opens a popover with the breakdown.\n *\n * One component in two placements rather than a second component, because the\n * figures, the bar's rules and every word are the same chip's (CLAUDE.md:\n * build once, add a prop). `collapsed` applies to the sidebar placement only;\n * the top bar has no rail.\n */\n placement?: 'sidebar' | 'topbar'\n /**\n * Where the popover's \"Open\" link goes -- the Wallet. `placement=\"topbar\"`\n * only. Omit it and the popover draws no link. The consumer supplies the URL;\n * Identity owns where the Wallet lives.\n */\n walletHref?: string\n /**\n * The element the popover's \"Open\" link renders through, so it routes\n * client-side with the locale prefix intact (ADR-0011). Defaults to `a`.\n */\n linkComponent?: ElementType\n /** Render the top-bar popover open on first paint -- the specimen's open state. */\n defaultOpen?: boolean\n /** The 56px rail. Abbreviates the balance and turns the split on its side. */\n collapsed?: boolean\n /**\n * Top up. Wiring for the one button the sidebar is allowed to have.\n *\n * **It is also what makes that button exist.** Omit it and the chip draws no\n * top-up affordance at all, in every state, and that is the fix B33-1270 made.\n * The render used to be gated on `!unknown` alone, so a consumer that passed\n * no handler got a visible control that did nothing and could not be removed\n * -- `.topUp`'s class name is CSS-Modules-hashed, so there is no selector to\n * reach it from outside the package.\n *\n * That was not hypothetical in either consumer. The Wallet passes no handler\n * and ships the dead button today\n * (`boostledger/src/app/(wallet)/WalletShell.tsx`), and Score wrote a whole\n * client module to arm it across a Server Component boundary rather than live\n * with it (`boostscore/components/app-shell/wallet-chip-top-up.tsx`, which\n * says so at length).\n *\n * Nothing renders in the button's place. It carries no information of its own\n * -- only its own affordance -- so a consumer with nowhere to send someone is\n * better served by the chip without it than by a disabled one they did not\n * ask for (ADR-0010, \"Not entitled is not disabled\", on why unavailable is not\n * the same as disabled). B33-1047 is the other half: an `href` form of the\n * same affordance, for a consumer that cannot pass a callback at all.\n */\n onTopUp?: () => void\n /**\n * The chip's user-facing strings. Every field has an English default, so a\n * consumer supplies only the ones it translates; the first two consumers are\n * French-default. The package takes no i18n dependency -- it renders what it\n * is given (B33-849).\n *\n * Every field is a plain string, so the whole object crosses the React Server\n * Component boundary and a Next App Router page can build it. The four labels\n * that carry a value mark its position with a `%placeholder%` (B33-1277). None\n * of them takes a function: that form only ever worked from a Client Component\n * and nothing in the type said so (B33-880, B33-1278).\n */\n labels?: Partial<WalletChipLabels>\n /**\n * The BCP-47 language tag the chip's figures are formatted in. Defaults to\n * `en-US`, which is what the chip rendered before this prop existed.\n *\n * `labels` gives the consumer the chip's words; this gives it their numbers.\n * French writes `4 600` where English writes `4,600`, and a label cannot fix\n * it -- the label functions receive the figures already formatted, so before\n * this prop there was no hook at all (B33-876). Grouping and the decimal\n * separator are CLDR facts about a locale rather than a property of the\n * consumer's sentence, so a tag is enough: `Intl.NumberFormat` knows the rest,\n * and a tag is a plain string, which a Server Component can pass where a\n * formatter function could not.\n *\n * It reaches the visible figures and, because the same strings are\n * interpolated into them, the `bar` and `railBalance` accessible names.\n *\n * The `k` after the rail's abbreviated balance is a word, not a number, so it\n * is `labels.railFigure` and not this.\n */\n locale?: string\n}\n\n/**\n * The chip with a balance the consumer stands behind. The two remaining figures\n * are required, because this is the state that renders them.\n */\nexport interface WalletChipKnownProps extends WalletChipChromeProps {\n /**\n * Absent or `false`. The discriminant: it is what makes the figures below\n * required here and forbidden in {@link WalletChipUnknownProps}.\n */\n unknown?: false\n /** Absent or `false`: see {@link WalletChipTotalOnlyProps}. */\n totalOnly?: false\n /** Remaining granted tokens. Granted burns first and expires. */\n granted: number\n /** Remaining purchased tokens. Purchased never expires. */\n purchased: number\n /**\n * What this cycle's grant started with, if the consumer knows. The bar's full\n * width alongside purchased, so the part already burned draws as empty track\n * and the filled portion shortens as the month runs.\n *\n * **Optional, and that is the fix B33-483 made.** It used to be required, and\n * no consumer could satisfy it honestly: it is the Ledger's\n * `grant_tokens_per_cycle`, and it cannot be reconstructed from a balance --\n * `cycleUsedPct` counts purchased-bucket spend, is not `(cycle total -\n * remaining)` once a credit, refund or void has touched the account, and is not\n * bounded at 100. So the Wallet passed `grantCycle = granted`, which pins\n * `burned` at 0 and held the threshold state unreachable in the reference\n * implementation, and Score passed a local per-plan belief instead.\n *\n * Omitted, the bar measures against `granted + purchased` -- the balance\n * itself. It then draws full and claims nothing about what burned, which is the\n * honest rendering when nobody can say.\n *\n * It no longer decides the threshold on its own: {@link burnedFraction} does\n * that, and this is only consulted when that is absent.\n */\n grantCycle?: number\n /**\n * How much of the cycle's grant has burned, as a fraction -- `0.88` for 88%.\n * Drives the threshold state, and is the figure the chip rounds into its pill.\n *\n * Passed in rather than derived, for exactly the reason {@link burnNote}\n * already is: the burn is the product's measurement to make, not the chip's.\n * The chip kept the arithmetic while taking the runway line as a prop, and fed\n * it from a denominator no product could supply -- so the measurement was the\n * chip's in name and nobody's in fact (B33-483).\n *\n * The *policy* stays here. `THRESHOLD_BURN` is 0.8 in this package, so four\n * products trip at the same place and cannot drift to four different numbers.\n * That is why this is a fraction and not a `threshold?: boolean`: a boolean\n * would move the 80% decision into each consumer, and it would leave the pill\n * with no figure to print.\n *\n * Supplied, it wins. Absent, the chip derives it from {@link grantCycle} as it\n * always did; absent with no `grantCycle` either, nothing is claimed and the\n * chip rests.\n *\n * It is not clamped. A consumer that passes a percentage where a fraction\n * belongs renders a visibly absurd pill rather than a plausible \"100%\", and a\n * wrong figure that looks wrong is the one that gets fixed.\n */\n burnedFraction?: number\n /** When the current grant expires, preformatted for display, e.g. \"30 Sep\". */\n grantExpires?: string\n /**\n * The threshold state's runway line, preformatted, e.g. \"About four days left\n * at your current burn.\" Passed in rather than derived: the burn estimate is\n * the product's to make, not the chip's -- the chip renders what it is given.\n */\n burnNote?: string\n}\n\n/**\n * The chip whose balance could not be confirmed. It renders an em dash, no bar\n * and no button, so **it takes no figures at all** -- each one below is\n * `?: never`.\n *\n * That is deliberate and it is the second half of B33-483. The figures were\n * required in every state, so both consumers manufactured zeroes to get past the\n * type and then apologised for them in a comment -- \"these zeroes are never\n * rendered ... but they are not a balance and must not be read as one\"\n * (`boostscore/components/app-shell/wallet-chip.tsx`), and the same again in\n * `wallet-slot.tsx` and `boostledger/src/app/(wallet)/layout.tsx`. A zero is a\n * confident claim about someone's money. The type now refuses it rather than\n * asking a comment to.\n *\n * `undefined` is still accepted, so a consumer that must build one props object\n * can write `{ unknown: true, granted: undefined }` instead of branching.\n */\nexport interface WalletChipUnknownProps extends WalletChipChromeProps {\n /** The balance could not be confirmed. Renders as unknown, never as zero. */\n unknown: true\n totalOnly?: false\n balance?: never\n granted?: never\n purchased?: never\n grantCycle?: never\n burnedFraction?: never\n /** Not rendered in this state: the footer only draws on a resting chip. */\n grantExpires?: never\n /** Not rendered in this state: the runway line only draws past the threshold. */\n burnNote?: never\n}\n\n/**\n * A discriminated union on `unknown`, and the discriminant has consequences for\n * how a consumer *types* its own code, not only what it passes:\n *\n * - A helper that returns props for both states needs an explicit\n * `: WalletChipProps` return annotation. Without one, TypeScript widens the\n * `unknown: true` in an object literal to `boolean`, which matches neither\n * member, and the spread at the call site stops compiling.\n * - `Omit<WalletChipProps, 'onTopUp'>` is not what it looks like. `Omit` over a\n * union flattens it into one object and loses the discrimination. Omit from\n * the members instead -- `Omit<WalletChipKnownProps, 'onTopUp'> |\n * Omit<WalletChipUnknownProps, 'onTopUp'>` -- which is why both are exported.\n */\nexport type WalletChipProps = WalletChipKnownProps | WalletChipUnknownProps | WalletChipTotalOnlyProps\n\n/**\n * The top-bar chip with a real total and no drawable split (B33-1712). Debits\n * draw the grant first, then purchased, and may run a set share past zero (the\n * Ledger's overdraft); credits add only to the bucket they name and never pay a\n * negative one off. So the Ledger can report a bucket below zero -- granted 0 /\n * purchased -40 (overdrawn, total -40), or granted 1000 / purchased -40 after\n * the month's grant lands (total 960, for the rest of the month). A two-segment\n * bar cannot draw a negative, and clamping it to zero would draw a split nobody\n * has: a split invented by the interface. So this state takes **the total and\n * no split**, and draws no bar and no granted / purchased legend.\n *\n * Below zero the figure is the distance past zero, positive, in --color-danger,\n * with the words \"past zero\" where a minus sign would be -- the Wallet's own\n * past-zero screen (boostledger, Paper 201 screen 03), followed rather than\n * inventing a third look. Nothing is broken and the copy does not say it is;\n * what happens next is the consumer's `burnNote`, because the overdraft limit is\n * the Ledger's. At zero or above it is the plain balance.\n *\n * **Top bar only.** The sidebar card has no drawing for this and the redesigned\n * shell does not render it, so the type requires `placement=\"topbar\"` rather\n * than leaving the card to guess. No board draws either form: they are new\n * undrawn states for the person at the gate to judge.\n */\nexport interface WalletChipTotalOnlyProps extends WalletChipChromeProps {\n placement: 'topbar'\n totalOnly: true\n unknown?: false\n /**\n * The balance, signed, in whole tokens: `granted + purchased` from the\n * Ledger's signed figures, which its single balance row guarantees is the\n * balance.\n */\n balance: number\n /**\n * What happens next, preformatted, e.g. \"Work carries on for another 930\n * tokens, then paid actions stop.\" The consumer's, because the overdraft limit\n * is the Ledger's, not the chip's.\n */\n burnNote?: string\n granted?: never\n purchased?: never\n grantCycle?: never\n burnedFraction?: never\n grantExpires?: never\n}\n\n/**\n * The granted/purchased bar. Granted is drawn first, because granted burns\n * first. The accessible label is passed in whole rather than assembled here:\n * word order is the consumer's, not English's (B33-849).\n */\nfunction Bar({ granted, purchased, cycleTotal, tone, label }: { granted: number; purchased: number; cycleTotal: number; tone: 'rest' | 'threshold'; label: string }) {\n return (\n <div className={styles.bar} data-tone={tone} role=\"img\" aria-label={label}>\n <span className={styles.segmentGranted} data-testid=\"segment-granted\" style={{ width: segmentWidth(granted, cycleTotal) }} />\n <span className={styles.segmentPurchased} data-testid=\"segment-purchased\" style={{ width: segmentWidth(purchased, cycleTotal) }} />\n </div>\n )\n}\n\nexport function WalletChip(props: WalletChipProps) {\n if (props.totalOnly === true) return <TotalOnlyChip {...props} />\n if (props.placement === 'topbar') return <TopBarChip {...props} />\n return <SidebarChip {...props} />\n}\n\n/**\n * The top-bar token chip (B33-1712). The pill shows the balance and the\n * two-segment bar; the popover (board 21) shows the figure, the bar again at\n * width, each kind of token with what it means, the runway line, and the buy\n * button.\n *\n * Both segments are always drawn and a zero one is 0px wide with the gap kept\n * -- board 22, and B33-590's rule that a zero is never drawn as a sliver. That\n * overrides B33-1712's line about copying the hub's 4px floor, by Jamal's\n * decision of 3 Nov 2026. A non-zero segment keeps the existing floor\n * (`segmentWidth`).\n *\n * Every figure is the consumer's. Unknown renders an em dash, no bar and no\n * breakdown -- a made-up split in a token balance is a commercial fact invented\n * by the interface (B33-1712).\n *\n * A disclosure, like the switcher (ADR-0010): the trigger is a button, the\n * panel carries no widget role, and dismissal is the shared anchored-overlay\n * contract's (ADR-0009).\n */\nfunction TopBarChip({ granted = 0, purchased = 0, grantExpires, burnNote, unknown = false, onTopUp, labels, locale = DEFAULT_WALLET_LOCALE, walletHref, linkComponent, defaultOpen }: WalletChipProps) {\n const t = resolveWalletChipLabels(labels)\n const overlay = useAnchoredOverlay({ defaultOpen })\n const Link: ElementType = linkComponent ?? 'a'\n const balance = granted + purchased\n // The pill and the popover draw the split of the balance itself: no burned\n // track, because the top bar claims nothing about what the cycle started\n // with. The sidebar card still does, from `grantCycle`.\n const total = balance\n const figure = unknown ? '—' : formatTokens(balance, locale)\n const buildBarLabel = () => t.bar(formatTokens(granted, locale), formatTokens(purchased, locale), formatTokens(balance, locale))\n\n return (\n <div className={styles.topRoot}>\n <button type=\"button\" className={styles.topTrigger} aria-label={unknown ? t.railBalanceUnknown : t.railBalance(figure)} onClick={overlay.toggle} {...overlay.triggerProps}>\n <span className={styles.topFigure} aria-hidden=\"true\">\n <span className={styles.topAmount}>{figure}</span>\n <span className={styles.topUnit}>{t.unit}</span>\n </span>\n {!unknown && <TopBar granted={granted} purchased={purchased} total={total} className={styles.topBar} />}\n <Chevron className={styles.topChevron} />\n </button>\n\n {overlay.open && (\n <div {...overlay.panelProps} className={styles.popover} aria-label={t.popover}>\n <div className={styles.popHead}>\n <span className={styles.popFigure}>\n <span className={styles.popAmount}>{figure}</span>\n <span className={styles.popUnit}>{t.unit}</span>\n </span>\n {walletHref !== undefined && (\n <Link className={buttons.quiet} href={walletHref} onClick={overlay.close}>\n {t.open}\n </Link>\n )}\n </div>\n\n {unknown ? (\n <p className={styles.popNote}>{t.balanceUnknown}</p>\n ) : (\n <>\n <TopBar granted={granted} purchased={purchased} total={total} className={styles.popBar} label={buildBarLabel()} />\n <ul className={styles.legend}>\n <li className={styles.legendRow}>\n <span className={styles.swatchGranted} aria-hidden=\"true\" />\n <span className={styles.legendText}>\n <span className={styles.legendFigure}>{t.grantedFigure(formatTokens(granted, locale))}</span>\n <span className={styles.legendNote}>{grantExpires ? t.grantedNote(grantExpires) : t.grantedNoteUndated}</span>\n </span>\n </li>\n <li className={styles.legendRow}>\n <span className={styles.swatchPurchased} aria-hidden=\"true\" />\n <span className={styles.legendText}>\n <span className={styles.legendFigure}>{t.purchasedFigure(formatTokens(purchased, locale))}</span>\n <span className={styles.legendNote}>{t.purchasedNote}</span>\n </span>\n </li>\n </ul>\n {(burnNote || onTopUp !== undefined) && (\n <div className={styles.popFoot}>\n {burnNote && <p className={styles.popNote}>{burnNote}</p>}\n {onTopUp !== undefined && (\n <button type=\"button\" className={`${buttons.primary} ${styles.buy}`} data-tone=\"accent\" onClick={onTopUp}>\n {t.buyTokens}\n </button>\n )}\n </div>\n )}\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * The top-bar chip with no drawable split (`WalletChipTotalOnlyProps`): the same\n * pill and popover as {@link TopBarChip}, with the total alone -- the distance\n * past zero when it is below -- and no bar and no legend anywhere.\n */\nfunction TotalOnlyChip({ balance, burnNote, onTopUp, labels, locale = DEFAULT_WALLET_LOCALE, walletHref, linkComponent, defaultOpen }: WalletChipTotalOnlyProps) {\n const t = resolveWalletChipLabels(labels)\n const overlay = useAnchoredOverlay({ defaultOpen })\n const Link: ElementType = linkComponent ?? 'a'\n const pastZero = balance < 0\n const figure = formatTokens(pastZero ? -balance : balance, locale)\n\n return (\n <div className={styles.topRoot} data-past-zero={pastZero ? 'true' : undefined}>\n <button type=\"button\" className={styles.topTrigger} aria-label={pastZero ? t.pastZeroBalance(figure) : t.railBalance(figure)} onClick={overlay.toggle} {...overlay.triggerProps}>\n <span className={styles.topFigure} aria-hidden=\"true\">\n <span className={styles.topAmount}>{figure}</span>\n <span className={styles.topUnit}>{pastZero ? t.pastZero : t.unit}</span>\n </span>\n <Chevron className={styles.topChevron} />\n </button>\n\n {overlay.open && (\n <div {...overlay.panelProps} className={styles.popover} aria-label={t.popover}>\n <div className={styles.popHead}>\n <span className={styles.popFigure}>\n <span className={styles.popAmount}>{figure}</span>\n <span className={styles.popUnit}>{pastZero ? t.pastZeroLong : t.unit}</span>\n </span>\n {walletHref !== undefined && (\n <Link className={buttons.quiet} href={walletHref} onClick={overlay.close}>\n {t.open}\n </Link>\n )}\n </div>\n {(burnNote || onTopUp !== undefined) && (\n <div className={styles.popFoot}>\n {burnNote && <p className={styles.popNote}>{burnNote}</p>}\n {onTopUp !== undefined && (\n <button type=\"button\" className={`${buttons.primary} ${styles.buy}`} data-tone=\"accent\" onClick={onTopUp}>\n {t.buyTokens}\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * The top bar's two-segment bar, in the pill and in the popover. Granted first,\n * because it burns first; a fixed gap between the two that stays when a segment\n * is zero, so a two-part balance never reads as one lump (board 22).\n */\nfunction TopBar({ granted, purchased, total, className, label }: { granted: number; purchased: number; total: number; className?: string; label?: string }) {\n return (\n <span className={className} role={label ? 'img' : undefined} aria-label={label} aria-hidden={label ? undefined : 'true'}>\n <span className={styles.segmentGranted} data-segment=\"granted\" style={{ width: segmentWidth(granted, total) }} />\n <span className={styles.segmentPurchased} data-segment=\"purchased\" style={{ width: segmentWidth(purchased, total) }} />\n </span>\n )\n}\n\nfunction SidebarChip({ granted = 0, purchased = 0, grantCycle, burnedFraction, grantExpires, burnNote, unknown = false, collapsed = false, onTopUp, labels, locale = DEFAULT_WALLET_LOCALE }: WalletChipProps) {\n const t = resolveWalletChipLabels(labels)\n // Assembled lazily: the unknown state draws no bar, and a consumer's `bar`\n // implementation can throw on the zeroes the destructure above defaults the\n // degraded path's absent figures to. Calling it only where a bar renders keeps\n // that crash surface off the render that exists precisely because fetching the\n // balance already failed (B33-875). B33-483 stopped the consumer having to pass\n // those zeroes; the chip still has to pick something, so the laziness stays.\n const buildBarLabel = () =>\n t.bar(formatTokens(granted, locale), formatTokens(purchased, locale), formatTokens(granted + purchased, locale))\n const balance = granted + purchased\n // Everything the cycle started with: the grant plus the purchased balance\n // sitting alongside it. Measuring against this is what makes the bar shorten\n // as tokens burn instead of only re-proportioning to a full width.\n //\n // With no `grantCycle` the denominator falls back to `granted + purchased`, so\n // the bar fills and there is no burned track -- the honest drawing when nobody\n // can say what the cycle started with (B33-483). The zero-division guard inside\n // segmentWidth still covers the case where that balance is itself zero.\n const cycleTotal = (grantCycle ?? granted) + purchased\n // How much of the cycle's grant has burned. The consumer's own figure wins;\n // `grantCycle` derives it exactly as it always did; with neither, nothing is\n // claimed and the chip rests. `grantCycle` is tested against undefined rather\n // than leaned on for falsiness, because 0 and \"not supplied\" both resolve to a\n // burn of 0 and must keep doing so.\n const derivedBurn = grantCycle !== undefined && grantCycle > 0 ? (grantCycle - granted) / grantCycle : 0\n const burned = burnedFraction ?? derivedBurn\n const isThreshold = !unknown && burned >= THRESHOLD_BURN\n\n if (collapsed) {\n // The abbreviated balance, assembled once: it is both the rail's visible\n // text and the figure the group's accessible name is built around, and a\n // sighted user and a screen-reader user must not be told different numbers.\n // Built only when a balance is known, for the same reason buildBarLabel is\n // lazy -- the unknown rail shows no figure, and a consumer's railFigure\n // should not run on a degraded path it never renders on (B33-875).\n const railBalance = unknown ? undefined : t.railFigure(thousandsFigure(balance, locale))\n return (\n <div className={styles.rail} role=\"group\" aria-label={railBalance === undefined ? t.railBalanceUnknown : t.railBalance(railBalance)} tabIndex={0}>\n <WalletMark className={styles.railIcon} />\n <span className={styles.railFigure}>{railBalance ?? '—'}</span>\n {unknown ? <span className={styles.srOnly}>{t.railUnknown}</span> : <Bar granted={granted} purchased={purchased} cycleTotal={cycleTotal} tone={isThreshold ? 'threshold' : 'rest'} label={buildBarLabel()} />}\n </div>\n )\n }\n\n return (\n <div className={styles.chip} role=\"group\" aria-label={t.region} tabIndex={0} data-threshold={isThreshold ? 'true' : undefined} data-unknown={unknown ? 'true' : undefined}>\n <div className={styles.header}>\n <span className={styles.name}>{t.name}</span>\n {isThreshold && <span className={styles.pill}>{Math.round(burned * 100)}%</span>}\n </div>\n\n {unknown ? (\n <div className={styles.figure}>\n <span className={styles.amount} aria-hidden=\"true\">\n {'—'}\n </span>\n <RetryMark className={styles.retry} />\n <span className={styles.srOnly}>{t.balanceUnknown}</span>\n </div>\n ) : (\n <div className={styles.figure}>\n <span className={styles.amount}>{formatTokens(balance, locale)}</span>\n <span className={styles.unit}>{t.unit}</span>\n </div>\n )}\n\n {unknown ? (\n <div className={styles.bar} data-tone=\"unknown\" aria-hidden=\"true\" />\n ) : (\n <Bar granted={granted} purchased={purchased} cycleTotal={cycleTotal} tone={isThreshold ? 'threshold' : 'rest'} label={buildBarLabel()} />\n )}\n\n {!unknown && !isThreshold && (\n <div className={styles.details}>\n <dl className={styles.split}>\n <div className={styles.splitRow}>\n <dt className={styles.splitLabel}>\n <span className={styles.dotGranted} aria-hidden=\"true\" />\n {t.granted}\n </dt>\n <dd className={styles.splitAmount}>{formatTokens(granted, locale)}</dd>\n </div>\n <div className={styles.splitRow}>\n <dt className={styles.splitLabel}>\n <span className={styles.dotPurchased} aria-hidden=\"true\" />\n {t.purchased}\n </dt>\n <dd className={styles.splitAmount}>{formatTokens(purchased, locale)}</dd>\n </div>\n </dl>\n {grantExpires && <p className={styles.footer}>{t.grantExpiry(grantExpires)}</p>}\n </div>\n )}\n\n {isThreshold && burnNote && <p className={styles.burnNote}>{burnNote}</p>}\n\n {/* A handler is what makes the button exist, not only what it does when\n pressed (B33-1270). `!unknown` alone drew it for a consumer with\n nowhere to send anyone, and a hashed class name left them no way to\n take it back out -- see `onTopUp`. Tested against `undefined` rather\n than for truthiness so that `onTopUp={undefined}`, which is what a\n consumer's own absent value looks like, reads the same as omitting it. */}\n {!unknown && onTopUp !== undefined && (\n <button\n type=\"button\"\n // Two tiers for one button (B33-1713, ADR-0016): at rest it is a word\n // in the chip's header, the quiet tier; past the threshold it grows\n // into the chip's one call to action, the primary tier in the accent\n // tone. `.topUp` adds only where it sits.\n className={`${isThreshold ? buttons.primary : buttons.quiet} ${styles.topUp}`}\n data-tone={isThreshold ? 'accent' : undefined}\n data-grown={isThreshold ? 'true' : undefined}\n onClick={onTopUp}\n >\n {t.topUp}\n </button>\n )}\n </div>\n )\n}\n"],"names":["THRESHOLD_BURN","SEGMENT_MIN","segmentWidth","amount","cycleTotal","share","Bar","granted","purchased","tone","label","jsxs","styles","jsx","WalletChip","props","TotalOnlyChip","TopBarChip","SidebarChip","grantExpires","burnNote","unknown","onTopUp","labels","locale","DEFAULT_WALLET_LOCALE","walletHref","linkComponent","defaultOpen","t","resolveWalletChipLabels","overlay","useAnchoredOverlay","Link","balance","total","figure","formatTokens","buildBarLabel","TopBar","Chevron","buttons","Fragment","pastZero","className","grantCycle","burnedFraction","collapsed","derivedBurn","burned","isThreshold","railBalance","thousandsFigure","WalletMark","RetryMark"],"mappings":"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"}
@@ -1,56 +1,102 @@
1
- const t = "_chip_xxga2_37", _ = "_header_xxga2_74", a = "_name_xxga2_83", n = "_pill_xxga2_162", s = "_figure_xxga2_171", e = "_amount_xxga2_186", o = "_unit_xxga2_195", x = "_retry_xxga2_200", r = "_bar_xxga2_209", i = "_segmentGranted_xxga2_230", c = "_segmentPurchased_xxga2_231", l = "_details_xxga2_275", g = "_split_xxga2_284", d = "_splitRow_xxga2_291", p = "_splitLabel_xxga2_297", u = "_splitAmount_xxga2_305", m = "_dotGranted_xxga2_313", h = "_dotPurchased_xxga2_314", b = "_footer_xxga2_328", f = "_burnNote_xxga2_336", y = "_topUp_xxga2_87", G = "_rail_xxga2_402", P = "_railIcon_xxga2_416", w = "_railFigure_xxga2_422", A = "_srOnly_xxga2_435", F = {
1
+ const t = "_chip_qvtyk_37", o = "_header_qvtyk_74", _ = "_name_qvtyk_83", e = "_pill_qvtyk_162", n = "_figure_qvtyk_171", p = "_amount_qvtyk_186", s = "_unit_qvtyk_195", r = "_retry_qvtyk_200", c = "_bar_qvtyk_209", a = "_segmentGranted_qvtyk_230", y = "_segmentPurchased_qvtyk_231", i = "_details_qvtyk_275", v = "_split_qvtyk_284", l = "_splitRow_qvtyk_291", d = "_splitLabel_qvtyk_297", k = "_splitAmount_qvtyk_305", q = "_dotGranted_qvtyk_313", u = "_dotPurchased_qvtyk_314", g = "_footer_qvtyk_328", h = "_burnNote_qvtyk_336", m = "_topUp_qvtyk_87", F = "_rail_qvtyk_387", b = "_railIcon_qvtyk_401", w = "_railFigure_qvtyk_407", A = "_srOnly_qvtyk_420", G = "_topRoot_qvtyk_444", N = "_topTrigger_qvtyk_453", P = "_topFigure_qvtyk_482", R = "_topAmount_qvtyk_489", U = "_topUnit_qvtyk_497", f = "_topChevron_qvtyk_508", B = "_topBar_qvtyk_518", T = "_popBar_qvtyk_519", x = "_popover_qvtyk_534", C = "_popHead_qvtyk_551", H = "_popFigure_qvtyk_558", I = "_popAmount_qvtyk_566", L = "_popUnit_qvtyk_575", O = "_legend_qvtyk_589", j = "_legendText_qvtyk_598", z = "_legendRow_qvtyk_603", D = "_swatchGranted_qvtyk_609", E = "_swatchPurchased_qvtyk_610", J = "_legendFigure_qvtyk_626", K = "_legendNote_qvtyk_634", M = "_popFoot_qvtyk_641", Q = "_popNote_qvtyk_649", S = "_buy_qvtyk_658", V = {
2
2
  chip: t,
3
- header: _,
4
- name: a,
5
- pill: n,
6
- figure: s,
7
- amount: e,
8
- unit: o,
9
- retry: x,
10
- bar: r,
11
- segmentGranted: i,
12
- segmentPurchased: c,
13
- details: l,
14
- split: g,
15
- splitRow: d,
16
- splitLabel: p,
17
- splitAmount: u,
18
- dotGranted: m,
19
- dotPurchased: h,
20
- footer: b,
21
- burnNote: f,
22
- topUp: y,
23
- rail: G,
24
- railIcon: P,
3
+ header: o,
4
+ name: _,
5
+ pill: e,
6
+ figure: n,
7
+ amount: p,
8
+ unit: s,
9
+ retry: r,
10
+ bar: c,
11
+ segmentGranted: a,
12
+ segmentPurchased: y,
13
+ details: i,
14
+ split: v,
15
+ splitRow: l,
16
+ splitLabel: d,
17
+ splitAmount: k,
18
+ dotGranted: q,
19
+ dotPurchased: u,
20
+ footer: g,
21
+ burnNote: h,
22
+ topUp: m,
23
+ rail: F,
24
+ railIcon: b,
25
25
  railFigure: w,
26
- srOnly: A
26
+ srOnly: A,
27
+ topRoot: G,
28
+ topTrigger: N,
29
+ topFigure: P,
30
+ topAmount: R,
31
+ topUnit: U,
32
+ topChevron: f,
33
+ topBar: B,
34
+ popBar: T,
35
+ popover: x,
36
+ popHead: C,
37
+ popFigure: H,
38
+ popAmount: I,
39
+ popUnit: L,
40
+ legend: O,
41
+ legendText: j,
42
+ legendRow: z,
43
+ swatchGranted: D,
44
+ swatchPurchased: E,
45
+ legendFigure: J,
46
+ legendNote: K,
47
+ popFoot: M,
48
+ popNote: Q,
49
+ buy: S
27
50
  };
28
51
  export {
29
- e as amount,
30
- r as bar,
31
- f as burnNote,
52
+ p as amount,
53
+ c as bar,
54
+ h as burnNote,
55
+ S as buy,
32
56
  t as chip,
33
- F as default,
34
- l as details,
35
- m as dotGranted,
36
- h as dotPurchased,
37
- s as figure,
38
- b as footer,
39
- _ as header,
40
- a as name,
41
- n as pill,
42
- G as rail,
57
+ V as default,
58
+ i as details,
59
+ q as dotGranted,
60
+ u as dotPurchased,
61
+ n as figure,
62
+ g as footer,
63
+ o as header,
64
+ O as legend,
65
+ J as legendFigure,
66
+ K as legendNote,
67
+ z as legendRow,
68
+ j as legendText,
69
+ _ as name,
70
+ e as pill,
71
+ I as popAmount,
72
+ T as popBar,
73
+ H as popFigure,
74
+ M as popFoot,
75
+ C as popHead,
76
+ Q as popNote,
77
+ L as popUnit,
78
+ x as popover,
79
+ F as rail,
43
80
  w as railFigure,
44
- P as railIcon,
45
- x as retry,
46
- i as segmentGranted,
47
- c as segmentPurchased,
48
- g as split,
49
- u as splitAmount,
50
- p as splitLabel,
51
- d as splitRow,
81
+ b as railIcon,
82
+ r as retry,
83
+ a as segmentGranted,
84
+ y as segmentPurchased,
85
+ v as split,
86
+ k as splitAmount,
87
+ d as splitLabel,
88
+ l as splitRow,
52
89
  A as srOnly,
53
- y as topUp,
54
- o as unit
90
+ D as swatchGranted,
91
+ E as swatchPurchased,
92
+ R as topAmount,
93
+ B as topBar,
94
+ f as topChevron,
95
+ P as topFigure,
96
+ G as topRoot,
97
+ N as topTrigger,
98
+ U as topUnit,
99
+ m as topUp,
100
+ s as unit
55
101
  };
56
102
  //# sourceMappingURL=WalletChip.module.css.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WalletChip.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"WalletChip.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -93,6 +93,53 @@ export interface WalletChipLabels {
93
93
  * `%granted% granted and %purchased% purchased of %remaining% remaining`.
94
94
  */
95
95
  bar: string;
96
+ /**
97
+ * The top-bar chip's popover, named for a screen reader: a disclosure panel
98
+ * carries no widget role, so it takes an `aria-label` (ADR-0010). Default
99
+ * "Token balance". `placement="topbar"` only (B33-1712).
100
+ */
101
+ popover: string;
102
+ /**
103
+ * The popover's granted line, around the preformatted amount (board 21:
104
+ * "3,420 granted"). Placeholder `%amount%`. Default `%amount% granted`.
105
+ */
106
+ grantedFigure: string;
107
+ /** The purchased line, on the same terms. Placeholder `%amount%`. Default `%amount% purchased`. */
108
+ purchasedFigure: string;
109
+ /**
110
+ * The note under the granted line when `grantExpires` is known (board 21).
111
+ * Placeholder `%date%`, the preformatted reset date. Default
112
+ * `Included each month. Resets %date%.`
113
+ */
114
+ grantedNote: string;
115
+ /**
116
+ * The same note when `grantExpires` is not supplied, so the chip never prints
117
+ * a reset date nobody gave it. Default `Included each month.`
118
+ */
119
+ grantedNoteUndated: string;
120
+ /** The note under the purchased line. Default `Yours to keep. These never expire.` */
121
+ purchasedNote: string;
122
+ /** The popover's link to the Wallet (`walletHref`). Default "Open". */
123
+ open: string;
124
+ /** The popover's buy button (`onTopUp`). Default "Buy tokens". */
125
+ buyTokens: string;
126
+ /**
127
+ * The top-bar chip's unit when the account is past zero (`totalOnly` with a negative `balance`,
128
+ * B33-1712), after the figure for how far past zero it is: "420 past zero".
129
+ * The Wallet's own past-zero screen says it in these words rather than with a
130
+ * minus sign, and the chip follows it. Default `past zero`.
131
+ */
132
+ pastZero: string;
133
+ /**
134
+ * The same, in the popover, where there is room for the noun. Default
135
+ * `tokens past zero`.
136
+ */
137
+ pastZeroLong: string;
138
+ /**
139
+ * The past-zero pill's accessible name. A template: `%amount%` is the
140
+ * formatted figure past zero. Default `Wallet balance %amount% tokens past zero`.
141
+ */
142
+ pastZeroBalance: string;
96
143
  }
97
144
  /**
98
145
  * The English defaults. A consumer that passes nothing renders exactly what the
@@ -128,6 +175,17 @@ export declare const defaultWalletChipLabels: Readonly<{
128
175
  railBalanceUnknown: string;
129
176
  railUnknown: string;
130
177
  bar: string;
178
+ popover: string;
179
+ grantedFigure: string;
180
+ purchasedFigure: string;
181
+ grantedNote: string;
182
+ grantedNoteUndated: string;
183
+ purchasedNote: string;
184
+ open: string;
185
+ buyTokens: string;
186
+ pastZero: string;
187
+ pastZeroLong: string;
188
+ pastZeroBalance: string;
131
189
  }>;
132
190
  /**
133
191
  * The labels as the render consumes them: the four templates presented as
@@ -140,11 +198,15 @@ export declare const defaultWalletChipLabels: Readonly<{
140
198
  * of it; and the call is where B33-875's laziness lives, so a label for an element
141
199
  * that does not render is not interpolated at all.
142
200
  */
143
- export interface ResolvedWalletChipLabels extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar'> {
201
+ export interface ResolvedWalletChipLabels extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar' | 'grantedFigure' | 'purchasedFigure' | 'grantedNote' | 'pastZeroBalance'> {
144
202
  grantExpiry: (date: string) => string;
203
+ grantedFigure: (amount: string) => string;
204
+ purchasedFigure: (amount: string) => string;
205
+ grantedNote: (date: string) => string;
145
206
  railFigure: (figure: string) => string;
146
207
  railBalance: (balance: string) => string;
147
208
  bar: (granted: string, purchased: string, remaining: string) => string;
209
+ pastZeroBalance: (amount: string) => string;
148
210
  }
149
211
  /**
150
212
  * Merge a consumer's partial `labels` onto the English defaults, treating an
@@ -1,5 +1,5 @@
1
1
  import { fillTemplate as n } from "../../fill-template.js";
2
- const c = Object.freeze({
2
+ const i = Object.freeze({
3
3
  region: "Wallet balance",
4
4
  name: "Wallet",
5
5
  unit: "tokens",
@@ -12,22 +12,37 @@ const c = Object.freeze({
12
12
  railBalance: "Wallet balance %balance%",
13
13
  railBalanceUnknown: "Wallet balance unknown",
14
14
  railUnknown: "unknown",
15
- bar: "%granted% granted and %purchased% purchased of %remaining% remaining"
15
+ bar: "%granted% granted and %purchased% purchased of %remaining% remaining",
16
+ popover: "Token balance",
17
+ grantedFigure: "%amount% granted",
18
+ purchasedFigure: "%amount% purchased",
19
+ grantedNote: "Included each month. Resets %date%.",
20
+ grantedNoteUndated: "Included each month.",
21
+ purchasedNote: "Yours to keep. These never expire.",
22
+ open: "Open",
23
+ buyTokens: "Buy tokens",
24
+ pastZero: "past zero",
25
+ pastZeroLong: "tokens past zero",
26
+ pastZeroBalance: "Wallet balance %amount% tokens past zero"
16
27
  });
17
28
  function p(r) {
18
- const l = Object.fromEntries(
29
+ const t = Object.fromEntries(
19
30
  Object.entries(r ?? {}).filter(([, e]) => e !== void 0)
20
- ), a = { ...c, ...l };
31
+ ), a = { ...i, ...t };
21
32
  return {
22
33
  ...a,
23
34
  grantExpiry: (e) => n(a.grantExpiry, { date: e }),
24
35
  railFigure: (e) => n(a.railFigure, { figure: e }),
25
36
  railBalance: (e) => n(a.railBalance, { balance: e }),
26
- bar: (e, t, i) => n(a.bar, { granted: e, purchased: t, remaining: i })
37
+ bar: (e, o, l) => n(a.bar, { granted: e, purchased: o, remaining: l }),
38
+ grantedFigure: (e) => n(a.grantedFigure, { amount: e }),
39
+ purchasedFigure: (e) => n(a.purchasedFigure, { amount: e }),
40
+ grantedNote: (e) => n(a.grantedNote, { date: e }),
41
+ pastZeroBalance: (e) => n(a.pastZeroBalance, { amount: e })
27
42
  };
28
43
  }
29
44
  export {
30
- c as defaultWalletChipLabels,
45
+ i as defaultWalletChipLabels,
31
46
  p as resolveWalletChipLabels
32
47
  };
33
48
  //# sourceMappingURL=labels.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"labels.js","sources":["../../../src/components/WalletChip/labels.ts"],"sourcesContent":["// The chip is the only component in the package that renders words. Every one\n// of them lives here as a prop with an English default, so a French-default\n// consumer (the first two are) can translate the chip without the package\n// taking an i18n dependency -- CLAUDE.md, \"a component here renders what it is\n// given\". This is the same argument grantExpires and burnNote already made,\n// applied to the strings that were still English literals in the render.\n//\n// Three of those strings carry a value, and word order is not a property of\n// English: \"Granted expires 30 Sep\" puts the date where English puts it, and a\n// translation cannot be reassembled from fragments on the consumer's side. They\n// are supplied whole, as a template that spells the value's position with a\n// `%placeholder%`.\n//\n// ── Every field is a string, and that is the whole type (B33-1278) ───────────\n//\n// Each of those three was once `string | ((…) => string)`. The function half is\n// gone, and the reason is that it could not be used by the consumers this package\n// has. A function is the one thing that cannot be a prop of a Client Component\n// rendered from a React Server Component: React's Flight serializer walks the\n// whole prop graph and throws on any function it finds, nested or not, in\n// production as well as in development:\n//\n// Functions cannot be passed directly to Client Components unless you\n// explicitly expose it by marking it with \"use server\".\n//\n// `WalletChip` is a client module and `AppShell.wallet` is a ReactNode slot, so\n// it is the *consumer's page* that constructs `<WalletChip …/>` -- and under the\n// App Router that page is a Server Component by default, which is how both\n// consumers render it, because reading the balance is a server read. So the\n// function half was type-legal and fatal in production for the only way anyone\n// uses the component, and nothing in the type said so: `tsc` accepted it, the dev\n// server accepted it, and it threw at serialisation time (B33-880 for the first\n// half of this, B33-1278 for the rest). Score covered the hole from outside, with\n// a runtime `typeof raw === 'string'` guard and five tests walking the prop graph,\n// because -- in its own words -- neither `tsc` nor the dev server will.\n//\n// What the function form was *for* was logic a template cannot express: ICU\n// plurals, an `Intl.ListFormat` call. That capability is not lost, it moves to\n// where ICU already lives. A consumer selecting a plural form does the selection\n// in its own catalogue and passes this package the finished sentence, which is\n// what both consumers already do with next-intl. No default label in this package\n// is count-dependent, so no default needed it (see `unit` below).\n//\n// B33-876 added a fourth of the same kind, `railFigure`. The rail's balance is\n// a number the `locale` prop formats -- \"4.6\" or \"4,6\" -- followed by a mark\n// that is a word and not a number: English \"k\", German \"Tsd.\", and French with\n// a space in front of it that English does not have. Intl cannot supply it (its\n// own compact notation renders German four-digit balances unabbreviated, which\n// does not fit a 56px rail -- see format.ts), so it belongs here, in the same\n// template shape, for the same reason: the consumer owns where the mark goes.\n\nimport { fillTemplate } from '../../fill-template'\n\n/**\n * Every user-facing string the chip renders. Supplied whole -- the strings that\n * carry a value take a `%placeholder%` template rather than fragments, because\n * word order is not a property of English and cannot be reassembled on the\n * consumer's side.\n *\n * **Every field is a `string`.** `grantExpiry`, `railFigure`, `railBalance` and\n * `bar` are templates: plain strings marking each value's position with a\n * `%placeholder%` -- `'Granted expires %date% · burns first'`. So the whole\n * `labels` object is data all the way down, it survives the React Server Component\n * boundary, and a Next App Router page can build it. There is no function form:\n * it only ever worked from a Client Component and nothing in the type said so\n * (B33-1278). A sentence needing logic a template cannot express -- a plural rule,\n * an `Intl.ListFormat` call -- is computed in the consumer's own catalogue, where\n * ICU already lives, and passed here finished.\n *\n * In a template, a `%name%` the label does not define is left exactly as written\n * -- a locale whose copy genuinely pairs two percent signs is not mangled, and a\n * misspelled placeholder shows up in the rendering rather than silently\n * blanking. A placeholder a template omits is simply not rendered: a locale that\n * does not need a figure is allowed not to name it.\n *\n * **The delimiter is `%name%`, not `{name}`** (B33-1277). A brace is ICU\n * MessageFormat argument syntax, so a consumer storing these templates in its own\n * next-intl catalogue could not read them back through its own formatter. It can\n * now. `src/fill-template.ts` carries the reasoning, including why `%{name}` and\n * `{{name}}` would both have kept the collision. A `{braced}` name in a template\n * is ordinary text here and is passed through untouched.\n */\nexport interface WalletChipLabels {\n /** Accessible name for the expanded chip's status region. Default \"Wallet balance\". */\n region: string\n /** The chip header. Default \"Wallet\". */\n name: string\n /**\n * The unit rendered beside the figure. Default \"tokens\".\n *\n * A single string, and so not count-dependent: a balance of one renders \"1\n * tokens\" in English. That has been true since B33-849 and B33-1278 did not\n * change it -- `unit` never had the function form, so dropping that form took\n * nothing away here. It is also a state the chip is unlikely to reach: a\n * one-token balance is an all-but-empty wallet. If a locale needs the unit to\n * agree with the figure, the shape to add is a count-aware field, which is a\n * decision and not a refactor; nothing in the package invents one meanwhile.\n */\n unit: string\n /** Screen-reader-only text in the figure when the balance is unknown. Default \"Balance unknown\". */\n balanceUnknown: string\n /** The granted side of the split -- granted burns first. Default \"Granted\". */\n granted: string\n /** The purchased side of the split -- purchased never expires. Default \"Purchased\". */\n purchased: string\n /**\n * The footer, around the preformatted expiry date. Supplied whole so the\n * consumer owns word order.\n *\n * Placeholder: `%date%`. Default (en) `Granted expires %date% · burns first`.\n */\n grantExpiry: string\n /** The top-up button. Default \"Top up\". */\n topUp: string\n /**\n * The rail's abbreviated balance, around the one-decimal-thousand figure\n * (e.g. \"4.6\"). Both the visible text in the 56px rail and the figure that\n * `railBalance` is given.\n *\n * Placeholder: `%figure%`. Default (en) `%figure%k`.\n *\n * The figure itself is formatted for `locale` -- \"4.6\" in `en-US`, \"4,6\" in\n * `fr-FR` -- but the mark after it is a word, and locales disagree about it:\n * French writes \"4,6 k\" with a space, German abbreviates a thousand \"Tsd.\".\n * So the mark is a label like every other word the chip renders, and a\n * template rather than a bare suffix, because a locale is entitled to put the\n * mark somewhere other than the end (B33-876).\n */\n railFigure: string\n /**\n * The rail group's accessible name, around the abbreviated balance (e.g. \"4.6k\").\n *\n * Placeholder: `%balance%`. Default (en) `Wallet balance %balance%`.\n */\n railBalance: string\n /** The rail group's accessible name when the balance is unknown. Default \"Wallet balance unknown\". */\n railBalanceUnknown: string\n /** Screen-reader-only marker on the rail when the balance is unknown. Default \"unknown\". */\n railUnknown: string\n /**\n * The bar's accessible label, around the three preformatted figures. Assembled\n * whole rather than concatenated from fragments -- the shape that cannot be\n * translated at all otherwise.\n *\n * Placeholders: `%granted%`, `%purchased%`, `%remaining%`. Default (en)\n * `%granted% granted and %purchased% purchased of %remaining% remaining`.\n */\n bar: string\n}\n\n/**\n * The English defaults. A consumer that passes nothing renders exactly what the\n * chip rendered before this prop existed, so the wallet baselines do not move.\n *\n * **Every field is a string, and now by construction.** That is what makes\n * `labels={defaultWalletChipLabels}` and\n * `labels={{ ...defaultWalletChipLabels, name: 'Portefeuille' }}` -- the pattern\n * the specimen shows and this package's own advice recommends -- legal from a\n * Server Component (B33-880). It used to be a runtime fact pinned by a test,\n * because `WalletChipLabels` admitted functions on four fields and `satisfies`\n * could not tell the two apart (B33-1029). With the union gone the compiler\n * carries it: a function here is a type error at the `satisfies`, and\n * `tests/wallet-chip-labels.type-test.ts` asserts the all-strings shape of the\n * interface itself so the guarantee cannot be lost by widening one field.\n *\n * Frozen because it is a public export reaching four products on a version bump.\n * `const` protects the binding, not the contents: without the freeze, a consumer\n * aliasing it by reference -- `const mine = defaultWalletChipLabels; mine.name =\n * 'Portefeuille'` -- would silently rename every chip in the process (B33-877).\n */\nexport const defaultWalletChipLabels = Object.freeze({\n region: 'Wallet balance',\n name: 'Wallet',\n unit: 'tokens',\n balanceUnknown: 'Balance unknown',\n granted: 'Granted',\n purchased: 'Purchased',\n grantExpiry: 'Granted expires %date% · burns first',\n topUp: 'Top up',\n railFigure: '%figure%k',\n railBalance: 'Wallet balance %balance%',\n railBalanceUnknown: 'Wallet balance unknown',\n railUnknown: 'unknown',\n bar: '%granted% granted and %purchased% purchased of %remaining% remaining',\n} satisfies WalletChipLabels)\n\n/**\n * The labels as the render consumes them: the four templates presented as\n * functions of their values, so the component writes `t.bar(…)` and never handles\n * a template itself.\n *\n * This is internal -- it is not exported from `src/index.ts` -- and it stays a\n * function shape now that `WalletChipLabels` holds only strings, for two reasons.\n * The interpolation has to happen somewhere, and one site per label is the whole\n * of it; and the call is where B33-875's laziness lives, so a label for an element\n * that does not render is not interpolated at all.\n */\nexport interface ResolvedWalletChipLabels\n extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar'> {\n grantExpiry: (date: string) => string\n railFigure: (figure: string) => string\n railBalance: (balance: string) => string\n bar: (granted: string, purchased: string, remaining: string) => string\n}\n\n/**\n * Merge a consumer's partial `labels` onto the English defaults, treating an\n * explicitly-`undefined` field as unsupplied, and present the four templates as\n * functions of their values.\n *\n * A next-intl `t('...')` returns `undefined` on a missed key, so\n * `{ name: undefined }` is what one missing translation out of thirteen looks like\n * at the call site -- it must fall back to English, not overwrite the default\n * with `undefined` and blank the header, unname the button, or hand\n * `fillTemplate` an `undefined` template (B33-873). A plain object spread copies\n * the `undefined` across; a per-field skip does not.\n */\nexport function resolveWalletChipLabels(labels?: Partial<WalletChipLabels>): ResolvedWalletChipLabels {\n const supplied = Object.fromEntries(\n Object.entries(labels ?? {}).filter(([, value]) => value !== undefined),\n ) as Partial<WalletChipLabels>\n const merged = { ...defaultWalletChipLabels, ...supplied }\n return {\n ...merged,\n grantExpiry: (date) => fillTemplate(merged.grantExpiry, { date }),\n railFigure: (figure) => fillTemplate(merged.railFigure, { figure }),\n railBalance: (balance) => fillTemplate(merged.railBalance, { balance }),\n bar: (granted, purchased, remaining) => fillTemplate(merged.bar, { granted, purchased, remaining }),\n }\n}\n"],"names":["defaultWalletChipLabels","resolveWalletChipLabels","labels","supplied","value","merged","date","fillTemplate","figure","balance","granted","purchased","remaining"],"mappings":";AA0KO,MAAMA,IAA0B,OAAO,OAAO;AAAA,EACnD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,KAAK;AACP,CAA4B;AAiCrB,SAASC,EAAwBC,GAA8D;AACpG,QAAMC,IAAW,OAAO;AAAA,IACtB,OAAO,QAAQD,KAAU,EAAE,EAAE,OAAO,CAAC,CAAA,EAAGE,CAAK,MAAMA,MAAU,MAAS;AAAA,EAAA,GAElEC,IAAS,EAAE,GAAGL,GAAyB,GAAGG,EAAA;AAChD,SAAO;AAAA,IACL,GAAGE;AAAA,IACH,aAAa,CAACC,MAASC,EAAaF,EAAO,aAAa,EAAE,MAAAC,GAAM;AAAA,IAChE,YAAY,CAACE,MAAWD,EAAaF,EAAO,YAAY,EAAE,QAAAG,GAAQ;AAAA,IAClE,aAAa,CAACC,MAAYF,EAAaF,EAAO,aAAa,EAAE,SAAAI,GAAS;AAAA,IACtE,KAAK,CAACC,GAASC,GAAWC,MAAcL,EAAaF,EAAO,KAAK,EAAE,SAAAK,GAAS,WAAAC,GAAW,WAAAC,EAAA,CAAW;AAAA,EAAA;AAEtG;"}
1
+ {"version":3,"file":"labels.js","sources":["../../../src/components/WalletChip/labels.ts"],"sourcesContent":["// The chip is the only component in the package that renders words. Every one\n// of them lives here as a prop with an English default, so a French-default\n// consumer (the first two are) can translate the chip without the package\n// taking an i18n dependency -- CLAUDE.md, \"a component here renders what it is\n// given\". This is the same argument grantExpires and burnNote already made,\n// applied to the strings that were still English literals in the render.\n//\n// Three of those strings carry a value, and word order is not a property of\n// English: \"Granted expires 30 Sep\" puts the date where English puts it, and a\n// translation cannot be reassembled from fragments on the consumer's side. They\n// are supplied whole, as a template that spells the value's position with a\n// `%placeholder%`.\n//\n// ── Every field is a string, and that is the whole type (B33-1278) ───────────\n//\n// Each of those three was once `string | ((…) => string)`. The function half is\n// gone, and the reason is that it could not be used by the consumers this package\n// has. A function is the one thing that cannot be a prop of a Client Component\n// rendered from a React Server Component: React's Flight serializer walks the\n// whole prop graph and throws on any function it finds, nested or not, in\n// production as well as in development:\n//\n// Functions cannot be passed directly to Client Components unless you\n// explicitly expose it by marking it with \"use server\".\n//\n// `WalletChip` is a client module and `AppShell.wallet` is a ReactNode slot, so\n// it is the *consumer's page* that constructs `<WalletChip …/>` -- and under the\n// App Router that page is a Server Component by default, which is how both\n// consumers render it, because reading the balance is a server read. So the\n// function half was type-legal and fatal in production for the only way anyone\n// uses the component, and nothing in the type said so: `tsc` accepted it, the dev\n// server accepted it, and it threw at serialisation time (B33-880 for the first\n// half of this, B33-1278 for the rest). Score covered the hole from outside, with\n// a runtime `typeof raw === 'string'` guard and five tests walking the prop graph,\n// because -- in its own words -- neither `tsc` nor the dev server will.\n//\n// What the function form was *for* was logic a template cannot express: ICU\n// plurals, an `Intl.ListFormat` call. That capability is not lost, it moves to\n// where ICU already lives. A consumer selecting a plural form does the selection\n// in its own catalogue and passes this package the finished sentence, which is\n// what both consumers already do with next-intl. No default label in this package\n// is count-dependent, so no default needed it (see `unit` below).\n//\n// B33-876 added a fourth of the same kind, `railFigure`. The rail's balance is\n// a number the `locale` prop formats -- \"4.6\" or \"4,6\" -- followed by a mark\n// that is a word and not a number: English \"k\", German \"Tsd.\", and French with\n// a space in front of it that English does not have. Intl cannot supply it (its\n// own compact notation renders German four-digit balances unabbreviated, which\n// does not fit a 56px rail -- see format.ts), so it belongs here, in the same\n// template shape, for the same reason: the consumer owns where the mark goes.\n\nimport { fillTemplate } from '../../fill-template'\n\n/**\n * Every user-facing string the chip renders. Supplied whole -- the strings that\n * carry a value take a `%placeholder%` template rather than fragments, because\n * word order is not a property of English and cannot be reassembled on the\n * consumer's side.\n *\n * **Every field is a `string`.** `grantExpiry`, `railFigure`, `railBalance` and\n * `bar` are templates: plain strings marking each value's position with a\n * `%placeholder%` -- `'Granted expires %date% · burns first'`. So the whole\n * `labels` object is data all the way down, it survives the React Server Component\n * boundary, and a Next App Router page can build it. There is no function form:\n * it only ever worked from a Client Component and nothing in the type said so\n * (B33-1278). A sentence needing logic a template cannot express -- a plural rule,\n * an `Intl.ListFormat` call -- is computed in the consumer's own catalogue, where\n * ICU already lives, and passed here finished.\n *\n * In a template, a `%name%` the label does not define is left exactly as written\n * -- a locale whose copy genuinely pairs two percent signs is not mangled, and a\n * misspelled placeholder shows up in the rendering rather than silently\n * blanking. A placeholder a template omits is simply not rendered: a locale that\n * does not need a figure is allowed not to name it.\n *\n * **The delimiter is `%name%`, not `{name}`** (B33-1277). A brace is ICU\n * MessageFormat argument syntax, so a consumer storing these templates in its own\n * next-intl catalogue could not read them back through its own formatter. It can\n * now. `src/fill-template.ts` carries the reasoning, including why `%{name}` and\n * `{{name}}` would both have kept the collision. A `{braced}` name in a template\n * is ordinary text here and is passed through untouched.\n */\nexport interface WalletChipLabels {\n /** Accessible name for the expanded chip's status region. Default \"Wallet balance\". */\n region: string\n /** The chip header. Default \"Wallet\". */\n name: string\n /**\n * The unit rendered beside the figure. Default \"tokens\".\n *\n * A single string, and so not count-dependent: a balance of one renders \"1\n * tokens\" in English. That has been true since B33-849 and B33-1278 did not\n * change it -- `unit` never had the function form, so dropping that form took\n * nothing away here. It is also a state the chip is unlikely to reach: a\n * one-token balance is an all-but-empty wallet. If a locale needs the unit to\n * agree with the figure, the shape to add is a count-aware field, which is a\n * decision and not a refactor; nothing in the package invents one meanwhile.\n */\n unit: string\n /** Screen-reader-only text in the figure when the balance is unknown. Default \"Balance unknown\". */\n balanceUnknown: string\n /** The granted side of the split -- granted burns first. Default \"Granted\". */\n granted: string\n /** The purchased side of the split -- purchased never expires. Default \"Purchased\". */\n purchased: string\n /**\n * The footer, around the preformatted expiry date. Supplied whole so the\n * consumer owns word order.\n *\n * Placeholder: `%date%`. Default (en) `Granted expires %date% · burns first`.\n */\n grantExpiry: string\n /** The top-up button. Default \"Top up\". */\n topUp: string\n /**\n * The rail's abbreviated balance, around the one-decimal-thousand figure\n * (e.g. \"4.6\"). Both the visible text in the 56px rail and the figure that\n * `railBalance` is given.\n *\n * Placeholder: `%figure%`. Default (en) `%figure%k`.\n *\n * The figure itself is formatted for `locale` -- \"4.6\" in `en-US`, \"4,6\" in\n * `fr-FR` -- but the mark after it is a word, and locales disagree about it:\n * French writes \"4,6 k\" with a space, German abbreviates a thousand \"Tsd.\".\n * So the mark is a label like every other word the chip renders, and a\n * template rather than a bare suffix, because a locale is entitled to put the\n * mark somewhere other than the end (B33-876).\n */\n railFigure: string\n /**\n * The rail group's accessible name, around the abbreviated balance (e.g. \"4.6k\").\n *\n * Placeholder: `%balance%`. Default (en) `Wallet balance %balance%`.\n */\n railBalance: string\n /** The rail group's accessible name when the balance is unknown. Default \"Wallet balance unknown\". */\n railBalanceUnknown: string\n /** Screen-reader-only marker on the rail when the balance is unknown. Default \"unknown\". */\n railUnknown: string\n /**\n * The bar's accessible label, around the three preformatted figures. Assembled\n * whole rather than concatenated from fragments -- the shape that cannot be\n * translated at all otherwise.\n *\n * Placeholders: `%granted%`, `%purchased%`, `%remaining%`. Default (en)\n * `%granted% granted and %purchased% purchased of %remaining% remaining`.\n */\n bar: string\n /**\n * The top-bar chip's popover, named for a screen reader: a disclosure panel\n * carries no widget role, so it takes an `aria-label` (ADR-0010). Default\n * \"Token balance\". `placement=\"topbar\"` only (B33-1712).\n */\n popover: string\n /**\n * The popover's granted line, around the preformatted amount (board 21:\n * \"3,420 granted\"). Placeholder `%amount%`. Default `%amount% granted`.\n */\n grantedFigure: string\n /** The purchased line, on the same terms. Placeholder `%amount%`. Default `%amount% purchased`. */\n purchasedFigure: string\n /**\n * The note under the granted line when `grantExpires` is known (board 21).\n * Placeholder `%date%`, the preformatted reset date. Default\n * `Included each month. Resets %date%.`\n */\n grantedNote: string\n /**\n * The same note when `grantExpires` is not supplied, so the chip never prints\n * a reset date nobody gave it. Default `Included each month.`\n */\n grantedNoteUndated: string\n /** The note under the purchased line. Default `Yours to keep. These never expire.` */\n purchasedNote: string\n /** The popover's link to the Wallet (`walletHref`). Default \"Open\". */\n open: string\n /** The popover's buy button (`onTopUp`). Default \"Buy tokens\". */\n buyTokens: string\n /**\n * The top-bar chip's unit when the account is past zero (`totalOnly` with a negative `balance`,\n * B33-1712), after the figure for how far past zero it is: \"420 past zero\".\n * The Wallet's own past-zero screen says it in these words rather than with a\n * minus sign, and the chip follows it. Default `past zero`.\n */\n pastZero: string\n /**\n * The same, in the popover, where there is room for the noun. Default\n * `tokens past zero`.\n */\n pastZeroLong: string\n /**\n * The past-zero pill's accessible name. A template: `%amount%` is the\n * formatted figure past zero. Default `Wallet balance %amount% tokens past zero`.\n */\n pastZeroBalance: string\n}\n\n/**\n * The English defaults. A consumer that passes nothing renders exactly what the\n * chip rendered before this prop existed, so the wallet baselines do not move.\n *\n * **Every field is a string, and now by construction.** That is what makes\n * `labels={defaultWalletChipLabels}` and\n * `labels={{ ...defaultWalletChipLabels, name: 'Portefeuille' }}` -- the pattern\n * the specimen shows and this package's own advice recommends -- legal from a\n * Server Component (B33-880). It used to be a runtime fact pinned by a test,\n * because `WalletChipLabels` admitted functions on four fields and `satisfies`\n * could not tell the two apart (B33-1029). With the union gone the compiler\n * carries it: a function here is a type error at the `satisfies`, and\n * `tests/wallet-chip-labels.type-test.ts` asserts the all-strings shape of the\n * interface itself so the guarantee cannot be lost by widening one field.\n *\n * Frozen because it is a public export reaching four products on a version bump.\n * `const` protects the binding, not the contents: without the freeze, a consumer\n * aliasing it by reference -- `const mine = defaultWalletChipLabels; mine.name =\n * 'Portefeuille'` -- would silently rename every chip in the process (B33-877).\n */\nexport const defaultWalletChipLabels = Object.freeze({\n region: 'Wallet balance',\n name: 'Wallet',\n unit: 'tokens',\n balanceUnknown: 'Balance unknown',\n granted: 'Granted',\n purchased: 'Purchased',\n grantExpiry: 'Granted expires %date% · burns first',\n topUp: 'Top up',\n railFigure: '%figure%k',\n railBalance: 'Wallet balance %balance%',\n railBalanceUnknown: 'Wallet balance unknown',\n railUnknown: 'unknown',\n bar: '%granted% granted and %purchased% purchased of %remaining% remaining',\n popover: 'Token balance',\n grantedFigure: '%amount% granted',\n purchasedFigure: '%amount% purchased',\n grantedNote: 'Included each month. Resets %date%.',\n grantedNoteUndated: 'Included each month.',\n purchasedNote: 'Yours to keep. These never expire.',\n open: 'Open',\n buyTokens: 'Buy tokens',\n pastZero: 'past zero',\n pastZeroLong: 'tokens past zero',\n pastZeroBalance: 'Wallet balance %amount% tokens past zero',\n} satisfies WalletChipLabels)\n\n/**\n * The labels as the render consumes them: the four templates presented as\n * functions of their values, so the component writes `t.bar(…)` and never handles\n * a template itself.\n *\n * This is internal -- it is not exported from `src/index.ts` -- and it stays a\n * function shape now that `WalletChipLabels` holds only strings, for two reasons.\n * The interpolation has to happen somewhere, and one site per label is the whole\n * of it; and the call is where B33-875's laziness lives, so a label for an element\n * that does not render is not interpolated at all.\n */\nexport interface ResolvedWalletChipLabels\n extends Omit<WalletChipLabels, 'grantExpiry' | 'railFigure' | 'railBalance' | 'bar' | 'grantedFigure' | 'purchasedFigure' | 'grantedNote' | 'pastZeroBalance'> {\n grantExpiry: (date: string) => string\n grantedFigure: (amount: string) => string\n purchasedFigure: (amount: string) => string\n grantedNote: (date: string) => string\n railFigure: (figure: string) => string\n railBalance: (balance: string) => string\n bar: (granted: string, purchased: string, remaining: string) => string\n pastZeroBalance: (amount: string) => string\n}\n\n/**\n * Merge a consumer's partial `labels` onto the English defaults, treating an\n * explicitly-`undefined` field as unsupplied, and present the four templates as\n * functions of their values.\n *\n * A next-intl `t('...')` returns `undefined` on a missed key, so\n * `{ name: undefined }` is what one missing translation out of thirteen looks like\n * at the call site -- it must fall back to English, not overwrite the default\n * with `undefined` and blank the header, unname the button, or hand\n * `fillTemplate` an `undefined` template (B33-873). A plain object spread copies\n * the `undefined` across; a per-field skip does not.\n */\nexport function resolveWalletChipLabels(labels?: Partial<WalletChipLabels>): ResolvedWalletChipLabels {\n const supplied = Object.fromEntries(\n Object.entries(labels ?? {}).filter(([, value]) => value !== undefined),\n ) as Partial<WalletChipLabels>\n const merged = { ...defaultWalletChipLabels, ...supplied }\n return {\n ...merged,\n grantExpiry: (date) => fillTemplate(merged.grantExpiry, { date }),\n railFigure: (figure) => fillTemplate(merged.railFigure, { figure }),\n railBalance: (balance) => fillTemplate(merged.railBalance, { balance }),\n bar: (granted, purchased, remaining) => fillTemplate(merged.bar, { granted, purchased, remaining }),\n grantedFigure: (amount) => fillTemplate(merged.grantedFigure, { amount }),\n purchasedFigure: (amount) => fillTemplate(merged.purchasedFigure, { amount }),\n grantedNote: (date) => fillTemplate(merged.grantedNote, { date }),\n pastZeroBalance: (amount) => fillTemplate(merged.pastZeroBalance, { amount }),\n }\n}\n"],"names":["defaultWalletChipLabels","resolveWalletChipLabels","labels","supplied","value","merged","date","fillTemplate","figure","balance","granted","purchased","remaining","amount"],"mappings":";AAyNO,MAAMA,IAA0B,OAAO,OAAO;AAAA,EACnD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,WAAW;AAAA,EACX,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,KAAK;AAAA,EACL,SAAS;AAAA,EACT,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,MAAM;AAAA,EACN,WAAW;AAAA,EACX,UAAU;AAAA,EACV,cAAc;AAAA,EACd,iBAAiB;AACnB,CAA4B;AAqCrB,SAASC,EAAwBC,GAA8D;AACpG,QAAMC,IAAW,OAAO;AAAA,IACtB,OAAO,QAAQD,KAAU,EAAE,EAAE,OAAO,CAAC,CAAA,EAAGE,CAAK,MAAMA,MAAU,MAAS;AAAA,EAAA,GAElEC,IAAS,EAAE,GAAGL,GAAyB,GAAGG,EAAA;AAChD,SAAO;AAAA,IACL,GAAGE;AAAA,IACH,aAAa,CAACC,MAASC,EAAaF,EAAO,aAAa,EAAE,MAAAC,GAAM;AAAA,IAChE,YAAY,CAACE,MAAWD,EAAaF,EAAO,YAAY,EAAE,QAAAG,GAAQ;AAAA,IAClE,aAAa,CAACC,MAAYF,EAAaF,EAAO,aAAa,EAAE,SAAAI,GAAS;AAAA,IACtE,KAAK,CAACC,GAASC,GAAWC,MAAcL,EAAaF,EAAO,KAAK,EAAE,SAAAK,GAAS,WAAAC,GAAW,WAAAC,EAAA,CAAW;AAAA,IAClG,eAAe,CAACC,MAAWN,EAAaF,EAAO,eAAe,EAAE,QAAAQ,GAAQ;AAAA,IACxE,iBAAiB,CAACA,MAAWN,EAAaF,EAAO,iBAAiB,EAAE,QAAAQ,GAAQ;AAAA,IAC5E,aAAa,CAACP,MAASC,EAAaF,EAAO,aAAa,EAAE,MAAAC,GAAM;AAAA,IAChE,iBAAiB,CAACO,MAAWN,EAAaF,EAAO,iBAAiB,EAAE,QAAAQ,GAAQ;AAAA,EAAA;AAEhF;"}
@@ -0,0 +1,5 @@
1
+ export type DeprecatedProp = 'TopBar.breadcrumb' | 'AppSwitcher.account';
2
+ /** Warn once, in development only, that a deprecated prop was passed. */
3
+ export declare function warnDeprecatedProp(prop: DeprecatedProp): void;
4
+ /** Test-only: forget which warnings have fired. */
5
+ export declare function resetDeprecatedPropWarnings(): void;
@@ -0,0 +1,11 @@
1
+ const t = /* @__PURE__ */ new Set(), o = {
2
+ "TopBar.breadcrumb": "@boost33/ui TopBar: `breadcrumb` (AppShell `topBar.breadcrumb`) is accepted and not drawn since 0.8.0 (B33-1712). Whatever it holds is not on screen. Move a sidebar toggle to `topBar.sidebarToggle` and an organisation or account switcher to AppShell `org`; the page title belongs in PageHeader. Then stop passing it.",
3
+ "AppSwitcher.account": "@boost33/ui AppSwitcher: `account` is accepted and not drawn since 0.8.0 (B33-1711). The account row is off the switcher. Put the organisation in AppShell `org`, the product's own pill at the left of the top bar. Then stop passing it."
4
+ };
5
+ function n(e) {
6
+ process.env.NODE_ENV !== "production" && (t.has(e) || (t.add(e), console.warn(o[e])));
7
+ }
8
+ export {
9
+ n as warnDeprecatedProp
10
+ };
11
+ //# sourceMappingURL=deprecated-prop-warning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deprecated-prop-warning.js","sources":["../src/deprecated-prop-warning.ts"],"sourcesContent":["// Development-only warnings for props 0.8.0 still accepts and no longer draws\n// (B33-1710). `topBar.breadcrumb` and the switcher's `account` both compile\n// unchanged, so a consumer upgrading from 0.7 loses what it put there -- a\n// sidebar toggle, an account switcher -- with no error and no type failure.\n// `@deprecated` strikes the prop through in an editor; this says so at runtime,\n// where an agent or a person running the app will see it.\n//\n// Called during render, not in an effect, so it works the same from a Server\n// Component (TopBar) as from a client one (AppSwitcher): on the server it lands\n// in the dev server's terminal, on the client in the browser console. Once per\n// prop per module instance, because a render runs many times and the message\n// does not change between them.\n//\n// `process.env.NODE_ENV` for the reason NavHeightBudgetWarning gives: every\n// bundler replaces that exact expression at build time and drops the branch,\n// which is how React ships its own dev warnings.\n\nconst warned = new Set<string>()\n\nexport type DeprecatedProp = 'TopBar.breadcrumb' | 'AppSwitcher.account'\n\nconst MESSAGES: Record<DeprecatedProp, string> = {\n 'TopBar.breadcrumb':\n '@boost33/ui TopBar: `breadcrumb` (AppShell `topBar.breadcrumb`) is accepted and not drawn since 0.8.0 ' +\n '(B33-1712). Whatever it holds is not on screen. Move a sidebar toggle to `topBar.sidebarToggle` and an ' +\n 'organisation or account switcher to AppShell `org`; the page title belongs in PageHeader. Then stop passing it.',\n 'AppSwitcher.account':\n '@boost33/ui AppSwitcher: `account` is accepted and not drawn since 0.8.0 (B33-1711). The account row is off ' +\n 'the switcher. Put the organisation in AppShell `org`, the product\\'s own pill at the left of the top bar. Then ' +\n 'stop passing it.',\n}\n\n/** Warn once, in development only, that a deprecated prop was passed. */\nexport function warnDeprecatedProp(prop: DeprecatedProp): void {\n if (process.env.NODE_ENV === 'production') return\n if (warned.has(prop)) return\n warned.add(prop)\n console.warn(MESSAGES[prop])\n}\n\n/** Test-only: forget which warnings have fired. */\nexport function resetDeprecatedPropWarnings(): void {\n warned.clear()\n}\n"],"names":["warned","MESSAGES","warnDeprecatedProp","prop"],"mappings":"AAiBA,MAAMA,wBAAa,IAAA,GAIbC,IAA2C;AAAA,EAC/C,qBACE;AAAA,EAGF,uBACE;AAGJ;AAGO,SAASC,EAAmBC,GAA4B;AAC7D,EAAI,QAAQ,IAAI,aAAa,iBACzBH,EAAO,IAAIG,CAAI,MACnBH,EAAO,IAAIG,CAAI,GACf,QAAQ,KAAKF,EAASE,CAAI,CAAC;AAC7B;"}
package/dist/index.d.ts CHANGED
@@ -11,17 +11,23 @@ export type { ProductMarkProps } from './components/ProductMark/ProductMark';
11
11
  export { NavItem } from './components/NavItem/NavItem';
12
12
  export type { NavItemProps } from './components/NavItem/NavItem';
13
13
  export { WalletChip } from './components/WalletChip/WalletChip';
14
- export type { WalletChipProps, WalletChipKnownProps, WalletChipUnknownProps } from './components/WalletChip/WalletChip';
14
+ export type { WalletChipProps, WalletChipKnownProps, WalletChipUnknownProps, WalletChipTotalOnlyProps } from './components/WalletChip/WalletChip';
15
15
  export { defaultWalletChipLabels } from './components/WalletChip/labels';
16
16
  export type { WalletChipLabels } from './components/WalletChip/labels';
17
17
  export { TopBar } from './components/TopBar/TopBar';
18
18
  export type { TopBarProps } from './components/TopBar/TopBar';
19
+ export { defaultTopBarLabels } from './components/TopBar/labels';
20
+ export type { TopBarLabels } from './components/TopBar/labels';
21
+ export { NotificationButton } from './components/NotificationButton/NotificationButton';
22
+ export type { NotificationButtonProps } from './components/NotificationButton/NotificationButton';
23
+ export { defaultNotificationButtonLabels } from './components/NotificationButton/labels';
24
+ export type { NotificationButtonLabels } from './components/NotificationButton/labels';
19
25
  export { PageHeader } from './components/PageHeader/PageHeader';
20
26
  export type { PageHeaderProps } from './components/PageHeader/PageHeader';
21
27
  export { AppShell } from './components/AppShell/AppShell';
22
- export type { AppShellProps, AppShellNavSection, AppShellNavItem } from './components/AppShell/AppShell';
28
+ export type { AppShellProps, AppShellAction, AppShellBreakpoint, AppShellNavSection, AppShellNavItem } from './components/AppShell/AppShell';
23
29
  export { AppSwitcher } from './components/AppSwitcher/AppSwitcher';
24
- export type { AppSwitcherProps, AppSwitcherProduct, AppSwitcherHub, AppSwitcherAccount, } from './components/AppSwitcher/AppSwitcher';
30
+ export type { AppSwitcherProps, AppSwitcherProduct, AppSwitcherHub, AppSwitcherAccount, AppSwitcherTrailing, } from './components/AppSwitcher/AppSwitcher';
25
31
  export { defaultAppSwitcherLabels } from './components/AppSwitcher/labels';
26
32
  export type { AppSwitcherLabels } from './components/AppSwitcher/labels';
27
33
  export { NotificationCenter } from './components/NotificationCenter/NotificationCenter';