@m3000/market 0.0.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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/blocks/auction/Auction.d.ts +49 -0
  3. package/dist/components/blocks/auction/Auction.js +44 -0
  4. package/dist/components/blocks/auction/AuctionBidForm.d.ts +11 -0
  5. package/dist/components/blocks/auction/AuctionBidForm.js +88 -0
  6. package/dist/components/blocks/auction/AuctionBidInput.d.ts +9 -0
  7. package/dist/components/blocks/auction/AuctionBidInput.js +99 -0
  8. package/dist/components/blocks/auction/AuctionContext.d.ts +71 -0
  9. package/dist/components/blocks/auction/AuctionContext.js +228 -0
  10. package/dist/components/blocks/auction/AuctionInfo.d.ts +9 -0
  11. package/dist/components/blocks/auction/AuctionInfo.js +37 -0
  12. package/dist/components/blocks/auction/AuctionLayout.d.ts +63 -0
  13. package/dist/components/blocks/auction/AuctionLayout.js +80 -0
  14. package/dist/components/blocks/auction/AuctionRankings.d.ts +16 -0
  15. package/dist/components/blocks/auction/AuctionRankings.js +334 -0
  16. package/dist/components/blocks/auction/AuctionStatusTag.d.ts +15 -0
  17. package/dist/components/blocks/auction/AuctionStatusTag.js +60 -0
  18. package/dist/components/blocks/auction/AuctionSuggestedBids.d.ts +38 -0
  19. package/dist/components/blocks/auction/AuctionSuggestedBids.js +116 -0
  20. package/dist/components/blocks/auction/AuctionYourBidCard.d.ts +27 -0
  21. package/dist/components/blocks/auction/AuctionYourBidCard.js +94 -0
  22. package/dist/components/blocks/auction/AuctionYourBids.d.ts +9 -0
  23. package/dist/components/blocks/auction/AuctionYourBids.js +49 -0
  24. package/dist/components/blocks/auction/index.d.ts +12 -0
  25. package/dist/components/blocks/index.d.ts +12 -0
  26. package/dist/components/index.d.ts +28 -0
  27. package/dist/components/primitives/Button.d.ts +31 -0
  28. package/dist/components/primitives/Button.js +117 -0
  29. package/dist/components/primitives/Drawer.d.ts +43 -0
  30. package/dist/components/primitives/Drawer.js +51 -0
  31. package/dist/components/primitives/Feedback.d.ts +28 -0
  32. package/dist/components/primitives/Feedback.js +147 -0
  33. package/dist/components/primitives/MorphDialog.d.ts +39 -0
  34. package/dist/components/primitives/MorphDialog.js +87 -0
  35. package/dist/components/primitives/Price.d.ts +84 -0
  36. package/dist/components/primitives/Price.js +255 -0
  37. package/dist/components/primitives/PriceInput.d.ts +33 -0
  38. package/dist/components/primitives/PriceInput.js +25 -0
  39. package/dist/components/primitives/Receipt.d.ts +164 -0
  40. package/dist/components/primitives/Receipt.js +344 -0
  41. package/dist/components/primitives/Scale.d.ts +67 -0
  42. package/dist/components/primitives/Scale.js +132 -0
  43. package/dist/components/primitives/Separator.d.ts +22 -0
  44. package/dist/components/primitives/Separator.js +62 -0
  45. package/dist/components/primitives/Skeleton.d.ts +14 -0
  46. package/dist/components/primitives/Skeleton.js +20 -0
  47. package/dist/components/primitives/SteppedInput.d.ts +94 -0
  48. package/dist/components/primitives/SteppedInput.js +154 -0
  49. package/dist/components/primitives/Tabs.d.ts +37 -0
  50. package/dist/components/primitives/Tabs.js +99 -0
  51. package/dist/components/primitives/Tag.d.ts +24 -0
  52. package/dist/components/primitives/Tag.js +22 -0
  53. package/dist/components/primitives/Text.d.ts +32 -0
  54. package/dist/components/primitives/Text.js +65 -0
  55. package/dist/components/primitives/countdown/Countdown.d.ts +24 -0
  56. package/dist/components/primitives/countdown/Countdown.js +22 -0
  57. package/dist/components/primitives/framed-image/FramedImage.d.ts +13 -0
  58. package/dist/components/primitives/framed-image/FramedImage.js +37 -0
  59. package/dist/components/primitives/index.d.ts +17 -0
  60. package/dist/components/primitives/ranked-list/Ranking.d.ts +117 -0
  61. package/dist/components/primitives/ranked-list/Ranking.js +219 -0
  62. package/dist/components/primitives/ranked-list/index.d.ts +1 -0
  63. package/dist/hooks/useCountdown.d.ts +20 -0
  64. package/dist/hooks/useCountdown.js +75 -0
  65. package/dist/index.d.ts +36 -0
  66. package/dist/index.js +36 -0
  67. package/dist/lib/cn.d.ts +6 -0
  68. package/dist/lib/cn.js +75 -0
  69. package/dist/lib/motion.d.ts +19 -0
  70. package/dist/lib/motion.js +43 -0
  71. package/dist/types/index.d.ts +120 -0
  72. package/dist/utils/format.d.ts +38 -0
  73. package/dist/utils/format.js +103 -0
  74. package/dist/utils/rank-utils.d.ts +34 -0
  75. package/dist/utils/rank-utils.js +80 -0
  76. package/dist/utils/tick-validation.d.ts +22 -0
  77. package/dist/utils/tick-validation.js +40 -0
  78. package/package.json +92 -0
  79. package/src/components/blocks/auction/Auction.tsx +74 -0
  80. package/src/components/blocks/auction/AuctionArtwork.tsx +4 -0
  81. package/src/components/blocks/auction/AuctionBidForm.tsx +138 -0
  82. package/src/components/blocks/auction/AuctionBidInput.tsx +166 -0
  83. package/src/components/blocks/auction/AuctionContext.tsx +401 -0
  84. package/src/components/blocks/auction/AuctionInfo.tsx +36 -0
  85. package/src/components/blocks/auction/AuctionLayout.tsx +200 -0
  86. package/src/components/blocks/auction/AuctionRankings.tsx +435 -0
  87. package/src/components/blocks/auction/AuctionStatusTag.tsx +98 -0
  88. package/src/components/blocks/auction/AuctionSuggestedBids.tsx +203 -0
  89. package/src/components/blocks/auction/AuctionYourBidCard.tsx +125 -0
  90. package/src/components/blocks/auction/AuctionYourBids.tsx +61 -0
  91. package/src/components/blocks/auction/index.ts +42 -0
  92. package/src/components/blocks/index.ts +1 -0
  93. package/src/components/index.ts +2 -0
  94. package/src/components/primitives/Button.tsx +183 -0
  95. package/src/components/primitives/Drawer.tsx +125 -0
  96. package/src/components/primitives/Feedback.tsx +185 -0
  97. package/src/components/primitives/MorphDialog.tsx +160 -0
  98. package/src/components/primitives/Price.tsx +394 -0
  99. package/src/components/primitives/PriceInput.tsx +48 -0
  100. package/src/components/primitives/Receipt.tsx +711 -0
  101. package/src/components/primitives/Scale.tsx +287 -0
  102. package/src/components/primitives/Separator.tsx +87 -0
  103. package/src/components/primitives/Skeleton.tsx +33 -0
  104. package/src/components/primitives/SteppedInput.tsx +313 -0
  105. package/src/components/primitives/Tabs.tsx +161 -0
  106. package/src/components/primitives/Tag.tsx +48 -0
  107. package/src/components/primitives/Text.tsx +102 -0
  108. package/src/components/primitives/countdown/Countdown.tsx +43 -0
  109. package/src/components/primitives/countdown/index.ts +2 -0
  110. package/src/components/primitives/framed-image/FramedImage.tsx +51 -0
  111. package/src/components/primitives/framed-image/index.ts +1 -0
  112. package/src/components/primitives/index.ts +42 -0
  113. package/src/components/primitives/ranked-list/RankedList.tsx +9 -0
  114. package/src/components/primitives/ranked-list/Ranking.tsx +454 -0
  115. package/src/components/primitives/ranked-list/index.ts +8 -0
  116. package/src/hooks/index.ts +1 -0
  117. package/src/hooks/useCountdown.ts +91 -0
  118. package/src/index.ts +130 -0
  119. package/src/lib/cn.ts +81 -0
  120. package/src/lib/index.ts +2 -0
  121. package/src/lib/motion.ts +55 -0
  122. package/src/public/lea-83-time-walk.png +0 -0
  123. package/src/public/lea-83-time-walk.webp +0 -0
  124. package/src/stories/Auction.stories.tsx +658 -0
  125. package/src/stories/AuctionLayout.stories.tsx +313 -0
  126. package/src/stories/AuctionStatusTag.stories.tsx +166 -0
  127. package/src/stories/AuctionYourBidCard.stories.tsx +257 -0
  128. package/src/stories/Button.stories.tsx +306 -0
  129. package/src/stories/Countdown.stories.tsx +158 -0
  130. package/src/stories/Feedback.stories.tsx +80 -0
  131. package/src/stories/FramedImage.stories.tsx +46 -0
  132. package/src/stories/MorphDialog.stories.tsx +88 -0
  133. package/src/stories/Price.stories.tsx +292 -0
  134. package/src/stories/RankedList.stories.tsx +190 -0
  135. package/src/stories/Receipt.stories.tsx +221 -0
  136. package/src/stories/Scale.stories.tsx +578 -0
  137. package/src/stories/Separator.stories.tsx +188 -0
  138. package/src/stories/Skeleton.stories.tsx +138 -0
  139. package/src/stories/SteppedInput.stories.tsx +321 -0
  140. package/src/stories/Tabs.stories.tsx +215 -0
  141. package/src/stories/Tag.stories.tsx +138 -0
  142. package/src/stories/Text.stories.tsx +245 -0
  143. package/src/styles/globals.css +39 -0
  144. package/src/styles/index.css +4 -0
  145. package/src/styles/theme/animation.css +11 -0
  146. package/src/styles/theme/color.css +185 -0
  147. package/src/styles/theme/index.css +3 -0
  148. package/src/styles/theme/typography.css +3 -0
  149. package/src/styles/utility.css +8 -0
  150. package/src/types/index.ts +149 -0
  151. package/src/utils/format.ts +130 -0
  152. package/src/utils/index.ts +16 -0
  153. package/src/utils/rank-utils.ts +131 -0
  154. package/src/utils/tick-validation.ts +65 -0
@@ -0,0 +1,203 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Button, Scale, Text } from "@/components/primitives";
5
+ import { cn } from "@/lib";
6
+ import { getProjectedRankForPrice } from "@/utils";
7
+ import { useAuctionContext } from "./AuctionContext";
8
+
9
+ export interface SuggestedBidContextValue {
10
+ value: bigint;
11
+ display: string;
12
+ position: number;
13
+ index: number;
14
+ isSelected: boolean;
15
+ onSelect: () => void;
16
+ disabled: boolean;
17
+ projectedRank: number | null;
18
+ isWinning: boolean;
19
+ }
20
+
21
+ const SuggestedBidContext =
22
+ React.createContext<SuggestedBidContextValue | null>(null);
23
+
24
+ function useSuggestedBid(): SuggestedBidContextValue {
25
+ const context = React.useContext(SuggestedBidContext);
26
+ if (!context) {
27
+ throw new Error(
28
+ "useSuggestedBid must be used within AuctionSuggestedBids.Item",
29
+ );
30
+ }
31
+ return context;
32
+ }
33
+
34
+ export interface AuctionSuggestedBidsProps {
35
+ className?: string;
36
+ count?: number;
37
+ children: (context: SuggestedBidContextValue) => React.ReactNode;
38
+ }
39
+
40
+ function AuctionSuggestedBidsRoot({
41
+ className,
42
+ count = 4,
43
+ children,
44
+ }: AuctionSuggestedBidsProps): React.ReactElement | null {
45
+ const {
46
+ minBidValue,
47
+ tickConfig,
48
+ tickSize,
49
+ isAuctionEnded,
50
+ bidValue,
51
+ setBidValue,
52
+ formatPrice,
53
+ currencySymbol,
54
+ bids,
55
+ setShowBidPreview,
56
+ lockedBid,
57
+ userBids,
58
+ mergedForRank,
59
+ maxTotalItems,
60
+ } = useAuctionContext();
61
+
62
+ const lockedBidId = React.useMemo(() => {
63
+ if (lockedBid === null) return null;
64
+ const lockedUserBid = userBids.find(
65
+ (ub) => ub.globalBidId === lockedBid.bidId,
66
+ );
67
+ return lockedUserBid?.id ?? null;
68
+ }, [lockedBid, userBids]);
69
+
70
+ const bidsForRankProjection = React.useMemo(() => {
71
+ if (lockedBidId === null) return mergedForRank;
72
+ return mergedForRank.filter((b) => b.id !== lockedBidId);
73
+ }, [mergedForRank, lockedBidId]);
74
+
75
+ const getProjectedRank = React.useCallback(
76
+ (priceValue: bigint) => {
77
+ return getProjectedRankForPrice(
78
+ priceValue,
79
+ bidsForRankProjection,
80
+ maxTotalItems,
81
+ );
82
+ },
83
+ [bidsForRankProjection, maxTotalItems],
84
+ );
85
+
86
+ if (isAuctionEnded) {
87
+ return null;
88
+ }
89
+
90
+ const getTickSize = (value: bigint): bigint => {
91
+ if (!tickConfig) return tickSize;
92
+ return value > tickConfig.threshold
93
+ ? tickConfig.largeTickSize
94
+ : tickConfig.smallTickSize;
95
+ };
96
+
97
+ const effectiveMinBid =
98
+ lockedBid !== null && lockedBid.priceValue > minBidValue
99
+ ? lockedBid.priceValue
100
+ : minBidValue;
101
+
102
+ const highestBid =
103
+ bids.length > 0 ? BigInt(bids[0].price) : effectiveMinBid * 3n;
104
+
105
+ const leadingBid = highestBid + getTickSize(highestBid);
106
+ const maxBid =
107
+ leadingBid > effectiveMinBid ? leadingBid : effectiveMinBid * 3n;
108
+
109
+ const selectedValue = bidValue;
110
+ const disabled = isAuctionEnded;
111
+
112
+ return (
113
+ <Scale.Linear
114
+ domain={[effectiveMinBid, maxBid]}
115
+ getTickSize={getTickSize}
116
+ snapMode="up"
117
+ className={cn("grid grid-cols-2 gap-2", className)}
118
+ >
119
+ <Scale.Ticks count={count}>
120
+ {(
121
+ { value, position }: { value: bigint; position: number },
122
+ index: number,
123
+ ) => {
124
+ const v = value;
125
+ const display = `${formatPrice(v)} ${currencySymbol}`;
126
+ const isSelected = v === selectedValue;
127
+ const { rank: projectedRank, isWinning } = getProjectedRank(v);
128
+
129
+ const contextValue: SuggestedBidContextValue = {
130
+ value: v,
131
+ display,
132
+ position,
133
+ index,
134
+ isSelected,
135
+ onSelect: () => {
136
+ setBidValue(v);
137
+ setShowBidPreview(true);
138
+ },
139
+ disabled,
140
+ projectedRank,
141
+ isWinning,
142
+ };
143
+
144
+ return (
145
+ <SuggestedBidContext.Provider value={contextValue}>
146
+ {children(contextValue)}
147
+ </SuggestedBidContext.Provider>
148
+ );
149
+ }}
150
+ </Scale.Ticks>
151
+ </Scale.Linear>
152
+ );
153
+ }
154
+
155
+ interface AuctionSuggestedBidsItemProps {
156
+ labels?: string[];
157
+ }
158
+
159
+ function AuctionSuggestedBidsItem({
160
+ labels = ["Minimum", "Safe Entry", "Competitive", "Leading"],
161
+ }: AuctionSuggestedBidsItemProps): React.ReactElement {
162
+ const context = useSuggestedBid();
163
+ const label = labels[context.index] ?? labels[labels.length - 1] ?? "Bid";
164
+
165
+ return (
166
+ <Button
167
+ type="button"
168
+ color="tertiary"
169
+ active={context.isSelected}
170
+ className="h-auto w-full items-start justify-between gap-2 px-3 py-2.5 text-left"
171
+ disabled={context.disabled}
172
+ onClick={context.onSelect}
173
+ size="card"
174
+ >
175
+ <div className="flex flex-col">
176
+ <Text color="secondary" size="1">
177
+ {label}
178
+ </Text>
179
+ <Text>{context.display}</Text>
180
+ </div>
181
+ {context.projectedRank != null && (
182
+ <span className="flex shrink-0 flex-col">
183
+ <Text color="secondary" size="1">
184
+ Rank
185
+ </Text>
186
+ <Text>#{context.projectedRank}</Text>
187
+ </span>
188
+ )}
189
+ </Button>
190
+ );
191
+ }
192
+
193
+ interface AuctionSuggestedBidsComponent {
194
+ Root: typeof AuctionSuggestedBidsRoot;
195
+ Item: typeof AuctionSuggestedBidsItem;
196
+ }
197
+
198
+ export const AuctionSuggestedBids: AuctionSuggestedBidsComponent = {
199
+ Root: AuctionSuggestedBidsRoot,
200
+ Item: AuctionSuggestedBidsItem,
201
+ };
202
+
203
+ export { useSuggestedBid };
@@ -0,0 +1,125 @@
1
+ "use client";
2
+
3
+ import { Button, Text } from "@/components/primitives";
4
+ import { cn } from "@/lib";
5
+ import type { AuctionUserBid } from "@/types";
6
+ import { formatDateTime } from "@/utils";
7
+
8
+ export interface AuctionYourBidCardProps {
9
+ bid: AuctionUserBid;
10
+ getRankForBid: (bidId: string) => number | null;
11
+ lockedBidId: bigint | null;
12
+ onLockForTopUp: (bidId: bigint, priceValue: bigint) => void;
13
+ onCancelTopUp: () => void;
14
+ isAuctionEnded: boolean;
15
+ onClaim?: (bidId: string) => Promise<boolean>;
16
+ formatPrice?: (priceValue: bigint) => string;
17
+ currencySymbol?: string;
18
+ }
19
+
20
+ function getBidStatusLabel(
21
+ status: AuctionUserBid["status"],
22
+ isWinning: boolean,
23
+ ): string {
24
+ if (status === "claimed") return "Won & Claimed";
25
+ if (isWinning) return "Winning";
26
+ if (status === "refunded") return "Refunded";
27
+ return "Outbid";
28
+ }
29
+
30
+ export function AuctionYourBidCard({
31
+ bid,
32
+ getRankForBid,
33
+ lockedBidId,
34
+ onLockForTopUp,
35
+ onCancelTopUp,
36
+ isAuctionEnded,
37
+ onClaim,
38
+ formatPrice = (v) =>
39
+ (Number(v) / 1e18).toLocaleString("en-US", {
40
+ minimumFractionDigits: 3,
41
+ maximumFractionDigits: 3,
42
+ }),
43
+ currencySymbol = "USD",
44
+ }: AuctionYourBidCardProps): React.ReactElement {
45
+ const rank = getRankForBid(bid.id);
46
+ const isLocked = lockedBidId !== null && bid.globalBidId === lockedBidId;
47
+
48
+ return (
49
+ <div
50
+ className={cn(
51
+ "rounded-xs border bg-muted p-4",
52
+ isLocked ? "border-success" : "border-transparent",
53
+ )}
54
+ >
55
+ <div className="flex items-start justify-between gap-2">
56
+ <Text size="1" color="tertiary">
57
+ Rank #{rank ?? "-"}
58
+ </Text>
59
+ <Text
60
+ size="1"
61
+ color="tertiary"
62
+ className="tabular-nums"
63
+ suppressHydrationWarning
64
+ >
65
+ {formatDateTime(bid.createdAt)}
66
+ </Text>
67
+ </div>
68
+ <div className="flex justify-between">
69
+ <Text size="2" className="mt-2">
70
+ {formatPrice(bid.price)} {currencySymbol}
71
+ </Text>
72
+ <div className="mt-2 flex items-center gap-1.5">
73
+ {bid.isWinning ? (
74
+ <>
75
+ <div className="size-4 rounded-full bg-success" aria-hidden />
76
+ <Text size="2" color="success">
77
+ {getBidStatusLabel(bid.status, bid.isWinning)}
78
+ </Text>
79
+ </>
80
+ ) : (
81
+ <>
82
+ <div className="size-4 rounded-full bg-destructive" aria-hidden />
83
+ <Text size="2" color="error">
84
+ {getBidStatusLabel(bid.status, bid.isWinning)}
85
+ </Text>
86
+ </>
87
+ )}
88
+ </div>
89
+ </div>
90
+ {bid.status === "claimed" ? (
91
+ <Text
92
+ size="1"
93
+ color="tertiary"
94
+ className="mt-2"
95
+ suppressHydrationWarning
96
+ >
97
+ Claimed {bid.claimedAt ? formatDateTime(bid.claimedAt) : ""}
98
+ </Text>
99
+ ) : bid.isWinning && bid.status === "active" && onClaim ? (
100
+ <Button
101
+ color="secondary"
102
+ className="mt-3 w-full"
103
+ onClick={() => onClaim(bid.id)}
104
+ >
105
+ Claim
106
+ </Button>
107
+ ) : null}
108
+ {bid.isWinning && !isAuctionEnded ? (
109
+ <div className="mt-3">
110
+ <Button
111
+ color="secondary"
112
+ className="w-full"
113
+ onClick={() =>
114
+ isLocked
115
+ ? onCancelTopUp()
116
+ : onLockForTopUp(bid.globalBidId, bid.price)
117
+ }
118
+ >
119
+ {isLocked ? "Cancel top-up" : "Top up"}
120
+ </Button>
121
+ </div>
122
+ ) : null}
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1,61 @@
1
+ "use client";
2
+
3
+ import { Text } from "@/components/primitives";
4
+ import { useAuctionContext } from "./AuctionContext";
5
+ import { AuctionYourBidCard } from "./AuctionYourBidCard";
6
+
7
+ export interface AuctionYourBidsProps {
8
+ className?: string;
9
+ }
10
+
11
+ export function AuctionYourBids({
12
+ className,
13
+ }: AuctionYourBidsProps): React.ReactElement | null {
14
+ const {
15
+ userBids,
16
+ getRankForBid,
17
+ lockedBid,
18
+ setLockedBid,
19
+ isAuctionEnded,
20
+ handleClaimEdition,
21
+ formatPrice,
22
+ currencySymbol,
23
+ } = useAuctionContext();
24
+
25
+ if (userBids.length === 0) return null;
26
+
27
+ const onLockForTopUp = (bidId: bigint, priceValue: bigint) => {
28
+ setLockedBid({ bidId, priceValue });
29
+ };
30
+
31
+ const onCancelTopUp = () => setLockedBid(null);
32
+
33
+ return (
34
+ <div className={className}>
35
+ <Text
36
+ as="h3"
37
+ color="tertiary"
38
+ className="mb-3 shrink-0"
39
+ aria-label={`Your Bids (${userBids.length})`}
40
+ >
41
+ Your Bids ({userBids.length})
42
+ </Text>
43
+ <div className="-mr-1 min-h-0 flex-1 space-y-3 overflow-y-auto py-1 pr-1">
44
+ {userBids.map((bid) => (
45
+ <AuctionYourBidCard
46
+ key={bid.id}
47
+ bid={bid}
48
+ getRankForBid={getRankForBid}
49
+ lockedBidId={lockedBid?.bidId ?? null}
50
+ onLockForTopUp={onLockForTopUp}
51
+ onCancelTopUp={onCancelTopUp}
52
+ isAuctionEnded={isAuctionEnded}
53
+ onClaim={handleClaimEdition}
54
+ formatPrice={formatPrice}
55
+ currencySymbol={currencySymbol}
56
+ />
57
+ ))}
58
+ </div>
59
+ </div>
60
+ );
61
+ }
@@ -0,0 +1,42 @@
1
+ export type { AuctionProps } from "./Auction";
2
+ export { Auction, useAuctionContext } from "./Auction";
3
+ export type { AuctionArtworkProps } from "./AuctionArtwork";
4
+ export { AuctionArtwork } from "./AuctionArtwork";
5
+ export { AuctionBidForm } from "./AuctionBidForm";
6
+ export type { AuctionBidInputProps } from "./AuctionBidInput";
7
+ export { AuctionBidInput } from "./AuctionBidInput";
8
+ export type { AuctionProviderProps } from "./AuctionContext";
9
+ export { AuctionProvider } from "./AuctionContext";
10
+ export type { AuctionInfoProps } from "./AuctionInfo";
11
+ export { AuctionInfo } from "./AuctionInfo";
12
+ export type {
13
+ AuctionBiddingPanelProps,
14
+ AuctionDetailsBodyProps,
15
+ AuctionDetailsFooterProps,
16
+ AuctionDetailsHeaderProps,
17
+ AuctionDetailsProps,
18
+ AuctionLayoutProps,
19
+ AuctionRankingsContainerProps,
20
+ } from "./AuctionLayout";
21
+ export {
22
+ AuctionBiddingPanel,
23
+ AuctionDetails,
24
+ AuctionDetailsBody,
25
+ AuctionDetailsFooter,
26
+ AuctionDetailsHeader,
27
+ AuctionLayout,
28
+ AuctionRankingsContainer,
29
+ } from "./AuctionLayout";
30
+ export type { AuctionRankingsProps } from "./AuctionRankings";
31
+ export { AuctionRankings, RankingsSkeleton } from "./AuctionRankings";
32
+ export type { AuctionStatusTagProps } from "./AuctionStatusTag";
33
+ export { AuctionStatusTag } from "./AuctionStatusTag";
34
+ export type {
35
+ AuctionSuggestedBidsProps,
36
+ SuggestedBidContextValue,
37
+ } from "./AuctionSuggestedBids";
38
+ export { AuctionSuggestedBids, useSuggestedBid } from "./AuctionSuggestedBids";
39
+ export type { AuctionYourBidCardProps } from "./AuctionYourBidCard";
40
+ export { AuctionYourBidCard } from "./AuctionYourBidCard";
41
+ export type { AuctionYourBidsProps } from "./AuctionYourBids";
42
+ export { AuctionYourBids } from "./AuctionYourBids";
@@ -0,0 +1 @@
1
+ export * from "./auction";
@@ -0,0 +1,2 @@
1
+ export * from "./blocks";
2
+ export * from "./primitives";
@@ -0,0 +1,183 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button";
2
+ import { IconLoader2 } from "@tabler/icons-react";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import type * as React from "react";
5
+ import { cn } from "@/lib/cn";
6
+
7
+ type ButtonColor =
8
+ | "primary"
9
+ | "secondary"
10
+ | "tertiary"
11
+ | "ghost"
12
+ | "destructive"
13
+ | "link";
14
+ type ButtonSize =
15
+ | "default"
16
+ | "xs"
17
+ | "sm"
18
+ | "lg"
19
+ | "xl"
20
+ | "icon"
21
+ | "icon-xs"
22
+ | "icon-sm"
23
+ | "icon-lg"
24
+ | "card";
25
+
26
+ interface ButtonVariantProps {
27
+ color?: ButtonColor;
28
+ size?: ButtonSize;
29
+ className?: string;
30
+ }
31
+
32
+ const buttonVariants = cva(
33
+ [
34
+ // Base layout & typography
35
+ "inline-flex cursor-pointer items-center justify-center gap-1.5",
36
+ "font-sans text-sm leading-none font-medium whitespace-nowrap",
37
+ "shrink-0 select-none",
38
+ // Transitions
39
+ "transition-all duration-150 ease-out",
40
+ // SVG children
41
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
42
+ // Focus ring
43
+ "outline-none",
44
+ "focus-visible:ring-2 focus-visible:ring-offset-2",
45
+ "focus-visible:ring-ring focus-visible:ring-offset-background",
46
+ // Disabled - prevent interaction
47
+ "disabled:pointer-events-none disabled:cursor-not-allowed aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed",
48
+ // aria-invalid
49
+ "aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/30",
50
+ ].join(" "),
51
+ {
52
+ variants: {
53
+ color: {
54
+ primary: [
55
+ "border border-solid",
56
+ "border-primary bg-primary text-primary-foreground",
57
+ "hover:border-primary-hover hover:bg-primary-hover",
58
+ "active:opacity-90",
59
+ "shadow-[0_1px_2px_0_rgb(0,0,0,0.08)]",
60
+ "disabled:opacity-50 aria-disabled:opacity-50",
61
+ ].join(" "),
62
+
63
+ secondary: [
64
+ "border border-solid",
65
+ "border-border bg-transparent text-foreground",
66
+ "hover:border-input hover:bg-accent",
67
+ "active:border-input active:bg-accent-active",
68
+ "data-[state=active]:border-input data-[state=active]:bg-accent-active",
69
+ "shadow-[0_1px_2px_0_rgb(0,0,0,0.04)]",
70
+ "disabled:opacity-50 aria-disabled:opacity-50",
71
+ ].join(" "),
72
+
73
+ tertiary: [
74
+ "border border-solid",
75
+ "border-border bg-muted text-muted-foreground",
76
+ "hover:border-input hover:bg-accent-hover hover:text-foreground",
77
+ "active:bg-accent-active active:text-foreground",
78
+ "data-[state=active]:bg-accent-active data-[state=active]:text-foreground",
79
+ "disabled:opacity-50 aria-disabled:opacity-50",
80
+ ].join(" "),
81
+
82
+ ghost: [
83
+ "border border-transparent bg-transparent",
84
+ "text-muted-foreground",
85
+ "hover:bg-accent hover:text-foreground",
86
+ "active:bg-accent-active active:text-foreground",
87
+ "data-[state=active]:bg-accent-active data-[state=active]:text-foreground",
88
+ "disabled:text-disabled-foreground",
89
+ "aria-disabled:text-disabled-foreground",
90
+ ].join(" "),
91
+
92
+ destructive: [
93
+ "border border-solid",
94
+ "border-destructive bg-destructive text-destructive-foreground",
95
+ "hover:border-destructive-hover hover:bg-destructive-hover",
96
+ "active:opacity-90",
97
+ "shadow-[0_1px_2px_0_rgb(0,0,0,0.08)]",
98
+ "disabled:opacity-50 aria-disabled:opacity-50",
99
+ ].join(" "),
100
+
101
+ link: [
102
+ "border-transparent bg-transparent",
103
+ "text-foreground underline-offset-4",
104
+ "hover:underline",
105
+ "h-auto px-0 py-0",
106
+ "disabled:text-disabled-foreground disabled:no-underline",
107
+ "aria-disabled:text-disabled-foreground aria-disabled:no-underline",
108
+ ].join(" "),
109
+ },
110
+
111
+ size: {
112
+ default: "h-9 rounded-md px-4 py-2 has-[>svg]:px-3",
113
+ xs: "h-6 gap-1 rounded px-2 text-xs has-[>svg]:px-1.5",
114
+ sm: "h-8 gap-1 rounded-md px-3 text-xs has-[>svg]:px-2.5",
115
+ lg: "h-10 rounded-lg px-5 text-[15px] has-[>svg]:px-4",
116
+ xl: "h-12 rounded-lg px-6 text-[15px] font-semibold has-[>svg]:px-5",
117
+ icon: "size-9 rounded-md",
118
+ "icon-xs": "size-6 rounded",
119
+ "icon-sm": "size-8 rounded-md",
120
+ "icon-lg": "size-10 rounded-lg",
121
+ card: "min-h-[4rem] rounded-md px-4 py-3",
122
+ },
123
+ },
124
+ defaultVariants: {
125
+ color: "primary",
126
+ size: "default",
127
+ },
128
+ },
129
+ ) as (props?: ButtonVariantProps) => string;
130
+
131
+ type ButtonVariants = typeof buttonVariants;
132
+
133
+ export type ButtonProps = React.ComponentProps<typeof ButtonPrimitive> &
134
+ VariantProps<ButtonVariants> & {
135
+ loading?: boolean;
136
+ active?: boolean;
137
+ };
138
+
139
+ export type ButtonElement = React.ComponentRef<"button">;
140
+
141
+ export function Button({
142
+ className,
143
+ color,
144
+ size,
145
+ disabled,
146
+ loading,
147
+ children,
148
+ active,
149
+ ...props
150
+ }: ButtonProps): React.ReactElement {
151
+ const _disabled = disabled || loading;
152
+
153
+ const _children = loading ? (
154
+ <>
155
+ <span className="invisible contents">{children}</span>
156
+ <span className="absolute inset-0 flex items-center justify-center">
157
+ <IconLoader2 aria-hidden className="size-4 animate-spin" />
158
+ </span>
159
+ </>
160
+ ) : (
161
+ children
162
+ );
163
+
164
+ return (
165
+ <ButtonPrimitive
166
+ data-state={active ? "active" : undefined}
167
+ disabled={_disabled}
168
+ focusableWhenDisabled
169
+ className={cn(
170
+ buttonVariants({ color, size }),
171
+ {
172
+ relative: loading,
173
+ },
174
+ className,
175
+ )}
176
+ {...props}
177
+ >
178
+ {_children}
179
+ </ButtonPrimitive>
180
+ );
181
+ }
182
+
183
+ export { buttonVariants };