@trading-game/design-intelligence-layer 0.13.3 → 0.14.0
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.
- package/README.md +14 -2
- package/dist/index.cjs +69 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +69 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -165,8 +165,9 @@ Blocks are pre-composed UI patterns built from design system primitives. They ar
|
|
|
165
165
|
|---|---|---|
|
|
166
166
|
| NavBar | Desktop, Mobile (closed), Mobile (open) | Landing page top navigation bar. Logo + ghost nav links (Products, Use Cases, Docs, Blog, FAQ) + Sign in / Sign up CTAs. |
|
|
167
167
|
| Hero | Type 1 (Desktop + Mobile), Type 2 | Landing page hero sections. Type 1: tagline pill + heading + body + CTAs left, image right (responsive). Type 2: centred single-column, tagline pill + heading + body + primary CTA with arrow icon, no image. |
|
|
168
|
-
| Sheet Open Positions | Active, Empty | Right-side sheet (desktop) / bottom drawer (mobile) listing open trading positions.
|
|
168
|
+
| Sheet Open Positions | Active (Rise-Fall, Swipe, Box-O, Digits), Empty, Edge cases (1 item, 30 items) | Right-side sheet (desktop) / bottom drawer (mobile) listing open trading positions. Each row shows bet name + stake (left) and PnL + market·duration (right). The drawer hugs its content with `max-h-[80vh]`; long lists scroll internally with header and footer pinned. |
|
|
169
169
|
| Header navigation | — | Product app top header bar. Back navigation button left, account balance + Demo badge centre, history and sound action buttons right. |
|
|
170
|
+
| Result | Win · continuing, Loss · continuing, End of demo, Out of balance, On a roll | Fixed-width (320 px) end-of-round result card. Thumb-up/down SVG with subtle radial halo + idle motion; `Bust.` / `Called it.` title becomes `You won!` / `You lost!`; amount headline; contract + duration `standard` Badge pills; primary + (optionally) secondary CTA. Out-of-balance and on-a-roll are dialog-style variants with no icon — just title + body + 2 CTAs. |
|
|
170
171
|
|
|
171
172
|
---
|
|
172
173
|
|
|
@@ -215,6 +216,9 @@ States: hover (`bg-secondary-hover`), focus (3px `ring-ring/50`), active (`opaci
|
|
|
215
216
|
<Badge variant="default-success" /> // Green
|
|
216
217
|
<Badge variant="default-fail" /> // Red
|
|
217
218
|
|
|
219
|
+
// Standard (neutral grey chip) — for non-status meta like contract type, duration
|
|
220
|
+
<Badge variant="standard" />
|
|
221
|
+
|
|
218
222
|
// Fill (tint background)
|
|
219
223
|
<Badge variant="fill" /> // Blue tint
|
|
220
224
|
<Badge variant="fill-success" /> // Green tint
|
|
@@ -595,7 +599,15 @@ Design tokens are managed in Figma and exported as CSS variables. To update:
|
|
|
595
599
|
|
|
596
600
|
## Changelog
|
|
597
601
|
|
|
598
|
-
###
|
|
602
|
+
### v0.14.0
|
|
603
|
+
- **New Result block:** Fixed-width (320 px) end-of-round result card with 5 variants — Win/continuing, Loss/continuing, End of demo, Out of balance, On a roll. Thumb-up/down SVG with subtle radial halo + idle motion (float for win, head-shake for loss). Title (`You won!` / `You lost!`), amount headline in semantic colour, contract + duration `standard` Badge pills, and CTA(s). Digit contracts render the picked digit inside the contract pill with a thin vertical divider. Out-of-balance and on-a-roll variants are dialog-only (no thumb / no amount) — title + body + 2 CTAs. Lives under **Blocks → Result** in the playground.
|
|
604
|
+
- **Sheet Open Positions redesign:** Each row now uses a 2-col layout — bet name + stake (left) / PnL + market·duration (right). Dropped per-row asset (`V100`), round number, and digit/multiplier suffix labels (the game is already implied by the sheet/tab title). Abbreviated durations (sec / min / hr / day) and dropped the `Stake ` prefix on amounts. Added an "Edge cases" row demonstrating drawer behavior with 1 item and 30 items.
|
|
605
|
+
- **Section nav sheet — desktop-safe:** The mobile section picker `Sheet` is now conditionally mounted (`{isMobile && …}`) so it can't be triggered at desktop widths where the permanent sidebar is already visible, avoiding redundant overlay layers.
|
|
606
|
+
- **Badge — new `standard` variant:** Neutral grey chip (`bg-subtle` / `text-on-prominent`) for non-status meta like contract type and duration. Existing `default` (solid blue) and all other variants are unchanged.
|
|
607
|
+
- **TicketCard / BoostTicketCard:**
|
|
608
|
+
- Left content restructured as a 2-column grid so the icon circle scales to match the **label + value height** (was a fixed `size-10` centered against the entire column).
|
|
609
|
+
- Boost badge now sits in row 2 of column 2 — aligned under the value, not pinned to the card's left edge.
|
|
610
|
+
- **2-decimal balance formatting:** the `value` prop is now normalised by `formatBalanceValue` before render, so values always display with exactly two decimal places regardless of input (e.g. `$12,450` → `$12,450.00`, `$12,450.5` → `$12,450.50`, `1234` → `1,234.00`). Currency prefix/suffix is preserved.
|
|
599
611
|
- **Token convention — `on-<surface>` foreground pairs:** Added a Material-style paired foreground for every semantic surface so colour roles are self-documenting and resolve robustly across consumer setups (incl. Module Federation, where raw `var()` fallbacks were previously needed). New CSS variables and Tailwind utilities:
|
|
600
612
|
- `--on-primary` (white) / `text-on-primary` — paired with `bg-primary`
|
|
601
613
|
- `--primary-inverse` (white) / `bg-primary-inverse` — inverted primary surface for use on dark/coloured areas
|
package/dist/index.cjs
CHANGED
|
@@ -947,6 +947,8 @@ var badgeVariants = (0, import_class_variance_authority3.cva)(
|
|
|
947
947
|
variant: {
|
|
948
948
|
// Default (solid)
|
|
949
949
|
default: "bg-primary text-on-primary [a&]:hover:bg-primary/90",
|
|
950
|
+
// Standard (neutral chip — for non-status meta like contract type, duration)
|
|
951
|
+
standard: "bg-subtle text-on-prominent [a&]:hover:bg-subtle/80",
|
|
950
952
|
"default-success": "bg-semantic-win text-on-semantic-win [a&]:hover:bg-semantic-win/90",
|
|
951
953
|
"default-fail": "bg-semantic-loss text-on-semantic-loss [a&]:hover:bg-semantic-loss/90",
|
|
952
954
|
"default-warning": "bg-semantic-warning text-on-semantic-warning [a&]:hover:bg-semantic-warning/90",
|
|
@@ -6650,6 +6652,18 @@ function TabsContent(_a) {
|
|
|
6650
6652
|
// components/ui/ticket-card.tsx
|
|
6651
6653
|
var import_lucide_react23 = require("lucide-react");
|
|
6652
6654
|
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
6655
|
+
function formatBalanceValue(raw) {
|
|
6656
|
+
const match = raw.match(/^(\D*)([\d,]+(?:\.\d+)?)(\D*)$/);
|
|
6657
|
+
if (!match) return raw;
|
|
6658
|
+
const [, prefix = "", numericPart, suffix = ""] = match;
|
|
6659
|
+
const parsed = Number.parseFloat(numericPart.replace(/,/g, ""));
|
|
6660
|
+
if (Number.isNaN(parsed)) return raw;
|
|
6661
|
+
const formatted = parsed.toLocaleString("en-US", {
|
|
6662
|
+
minimumFractionDigits: 2,
|
|
6663
|
+
maximumFractionDigits: 2
|
|
6664
|
+
});
|
|
6665
|
+
return `${prefix}${formatted}${suffix}`;
|
|
6666
|
+
}
|
|
6653
6667
|
function TicketCard({
|
|
6654
6668
|
className,
|
|
6655
6669
|
icon,
|
|
@@ -6667,7 +6681,7 @@ function TicketCard({
|
|
|
6667
6681
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex h-11 flex-col justify-center gap-1 whitespace-nowrap", children: [
|
|
6668
6682
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("p", { className: "text-xs text-on-subtle", children: label }),
|
|
6669
6683
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("p", { className: "leading-none", children: [
|
|
6670
|
-
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-[20px] font-bold text-on-prominent", children: value }),
|
|
6684
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-[20px] font-bold text-on-prominent", children: formatBalanceValue(value) }),
|
|
6671
6685
|
currency && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
6672
6686
|
" ",
|
|
6673
6687
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-sm text-on-subtle", children: currency })
|
|
@@ -6738,40 +6752,62 @@ function BoostTicketCard({
|
|
|
6738
6752
|
compact = false
|
|
6739
6753
|
}) {
|
|
6740
6754
|
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: cn("flex w-full flex-col gap-2", className), children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "relative flex w-full items-stretch justify-between rounded-sm bg-subtle", children: [
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6755
|
+
(() => {
|
|
6756
|
+
const boostBadge = /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Badge, { variant: "fill-boost", size: "sm", className: "!gap-1 !font-medium !tracking-normal", children: [
|
|
6757
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_lucide_react23.Rocket, { className: "!size-3.5", strokeWidth: 2 }),
|
|
6758
|
+
"Boost: ",
|
|
6759
|
+
boostAmount,
|
|
6760
|
+
" ",
|
|
6761
|
+
boostCurrency,
|
|
6762
|
+
boostInfo && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Tooltip2, { children: [
|
|
6763
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
6764
|
+
"button",
|
|
6765
|
+
{
|
|
6766
|
+
type: "button",
|
|
6767
|
+
"aria-label": "More info about boost",
|
|
6768
|
+
className: "inline-flex shrink-0 items-center justify-center rounded-full text-on-semantic-boost outline-none transition-opacity hover:opacity-70 focus-visible:opacity-70 focus-visible:ring-2 focus-visible:ring-on-semantic-boost/40 cursor-pointer",
|
|
6769
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_lucide_react23.Info, { className: "!size-3.5", strokeWidth: 2 })
|
|
6770
|
+
}
|
|
6771
|
+
) }),
|
|
6772
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TooltipContent, { side: boostInfoSide, align: boostInfoAlign, title: boostInfoTitle, closeLabel: boostInfoCloseLabel, variant: "inverse", className: "max-w-xs whitespace-normal", children: boostInfo })
|
|
6773
|
+
] }) })
|
|
6774
|
+
] });
|
|
6775
|
+
const labelValue = /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
6776
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("p", { className: "text-xs text-on-subtle", children: label }),
|
|
6777
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("p", { className: "leading-none", children: [
|
|
6778
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-[20px] font-bold text-on-prominent", children: formatBalanceValue(value) }),
|
|
6779
|
+
currency && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
6780
|
+
" ",
|
|
6781
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-sm text-on-subtle", children: currency })
|
|
6752
6782
|
] })
|
|
6753
|
-
] }),
|
|
6754
|
-
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Badge, { variant: "fill-boost", size: "sm", className: "!gap-1 !font-medium !tracking-normal", children: [
|
|
6755
|
-
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_lucide_react23.Rocket, { className: "!size-3.5", strokeWidth: 2 }),
|
|
6756
|
-
"Boost: ",
|
|
6757
|
-
boostAmount,
|
|
6758
|
-
" ",
|
|
6759
|
-
boostCurrency,
|
|
6760
|
-
boostInfo && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Tooltip2, { children: [
|
|
6761
|
-
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
6762
|
-
"button",
|
|
6763
|
-
{
|
|
6764
|
-
type: "button",
|
|
6765
|
-
"aria-label": "More info about boost",
|
|
6766
|
-
className: "inline-flex shrink-0 items-center justify-center rounded-full text-on-semantic-boost outline-none transition-opacity hover:opacity-70 focus-visible:opacity-70 focus-visible:ring-2 focus-visible:ring-on-semantic-boost/40 cursor-pointer",
|
|
6767
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_lucide_react23.Info, { className: "!size-3.5", strokeWidth: 2 })
|
|
6768
|
-
}
|
|
6769
|
-
) }),
|
|
6770
|
-
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(TooltipContent, { side: boostInfoSide, align: boostInfoAlign, title: boostInfoTitle, closeLabel: boostInfoCloseLabel, variant: "inverse", className: "max-w-xs whitespace-normal", children: boostInfo })
|
|
6771
|
-
] }) })
|
|
6772
6783
|
] })
|
|
6773
|
-
] })
|
|
6774
|
-
|
|
6784
|
+
] });
|
|
6785
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: cn("flex flex-1 items-center overflow-hidden py-4", compact ? "gap-2 px-2" : "gap-4 px-4"), children: [
|
|
6786
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "flex size-10 shrink-0 items-center justify-center rounded-full border-2 border-primary text-primary", children: icon }),
|
|
6787
|
+
compact ? (
|
|
6788
|
+
/* Mobile / compact: badge stacked under the value */
|
|
6789
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex flex-col gap-2 whitespace-nowrap", children: [
|
|
6790
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "flex flex-col gap-1", children: labelValue }),
|
|
6791
|
+
boostBadge
|
|
6792
|
+
] })
|
|
6793
|
+
) : (
|
|
6794
|
+
/* Desktop: badge inline next to the value (16px gap = gap-4) */
|
|
6795
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex flex-col gap-1 whitespace-nowrap", children: [
|
|
6796
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("p", { className: "text-xs text-on-subtle", children: label }),
|
|
6797
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex items-center gap-4", children: [
|
|
6798
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("p", { className: "leading-none", children: [
|
|
6799
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-[20px] font-bold text-on-prominent", children: formatBalanceValue(value) }),
|
|
6800
|
+
currency && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
6801
|
+
" ",
|
|
6802
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-sm text-on-subtle", children: currency })
|
|
6803
|
+
] })
|
|
6804
|
+
] }),
|
|
6805
|
+
boostBadge
|
|
6806
|
+
] })
|
|
6807
|
+
] })
|
|
6808
|
+
)
|
|
6809
|
+
] });
|
|
6810
|
+
})(),
|
|
6775
6811
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { "aria-hidden": true, className: "pointer-events-none absolute right-[88px] top-[-12px] z-10 size-6 rounded-full bg-prominent" }),
|
|
6776
6812
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { "aria-hidden": true, className: "pointer-events-none absolute right-[88px] bottom-[-12px] z-10 size-6 rounded-full bg-prominent" }),
|
|
6777
6813
|
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|