UNPKG

8.94 kBPlain TextView Raw
1import { css, customElement, html, LitElement, property } from 'lit-element'
2
3import { classMap } from 'lit-html/directives/class-map'
4import { styleMap } from 'lit-html/directives/style-map'
5
6import {
7 Network,
8 OpenSeaAsset,
9 OpenSeaCollection,
10 OpenSeaFungibleToken,
11} from 'opensea-js/lib/types'
12/* lit-element classes */
13import './info-button'
14import { toBaseDenomination } from './utils'
15import { PriceType, State } from './types'
16
17@customElement('nft-card-front')
18export class NftCardFrontTemplate extends LitElement {
19 @property({ type: Object }) public asset?: OpenSeaAsset
20 @property({ type: Boolean }) public horizontal!: boolean
21 @property({ type: Object }) public state!: State
22 @property({ type: Boolean }) public flippedCard: boolean = false
23
24 static get styles() {
25 return css`
26 .card-front.is-flipped {
27 display: none;
28 }
29 .card-front {
30 position: absolute;
31 backface-visibility: hidden;
32 background: #ffffff;
33 border-radius: 5px;
34 display: grid;
35 grid-template-columns: 1fr 2fr;
36 position: relative;
37 width: 100%;
38 height: 100%;
39 transform: translateY(0);
40 overflow: hidden;
41 }
42 .is-vertical {
43 grid-template-columns: 100%;
44 grid-template-rows: 60% 40%;
45 }
46 .card-front p {
47 margin: 0;
48 }
49
50 .asset-image-container {
51 border-right: 1px solid #e2e6ef;
52 background-size: cover;
53 box-sizing: border-box;
54 }
55
56 .asset-image {
57 background-size: contain;
58 background-position: 50%;
59 background-repeat: no-repeat;
60 height: 100%;
61 box-sizing: border-box;
62 }
63
64 .is-vertical .asset-image-container {
65 border-bottom: 1px solid #e2e6ef;
66 border-right: none;
67 width: 100%;
68 }
69
70 .asset-details-container {
71 display: grid;
72 grid-template-rows: auto;
73 grid-template-columns: 1fr 1fr;
74 padding: 20px;
75 align-items: center;
76 }
77 .asset-detail {
78 display: flex;
79 }
80 .asset-detail .asset-detail-type {
81 height: 35px;
82 font-size: 12px;
83 margin-right: 10px;
84 }
85 .asset-detail .asset-detail-badge {
86 width: 54px;
87 height: 30px;
88 font-size: 12px;
89 }
90 .asset-detail-name {
91 font-weight: 400;
92 text-align: left;
93 }
94 .asset-detail-price {
95 align-items: flex-end;
96 font-size: 18px;
97 font-weight: 400;
98 display: flex;
99 flex-flow: row;
100 justify-content: flex-end;
101 line-height: 15px;
102 text-align: right;
103 padding: 6px 0;
104 }
105 .asset-detail-price img {
106 margin: 0 4px;
107 }
108 .asset-detail-price-current img {
109 width: 15px;
110 }
111 .asset-detail-price-previous {
112 font-size: 14px;
113 color: rgb(130, 130, 130);
114 line-height: 10px;
115 }
116 .asset-detail-price-previous img {
117 width: 1ex;
118 }
119 .asset-detail-price .value {
120 margin-left: 5px;
121 }
122 .asset-detail-price .previous-value {
123 font-size: 14px;
124 color: #828282;
125 }
126 .asset-action-buy {
127 grid-column-start: 1;
128 grid-column-end: 3;
129 }
130 .asset-action-buy button {
131 width: 100%;
132 background: #3291e9;
133 border-radius: 5px;
134 height: 35px;
135 color: white;
136 font-weight: bold;
137 letter-spacing: 0.5px;
138 cursor: pointer;
139 transition: 200ms;
140 outline: none;
141 border-style: none;
142 text-transform: uppercase;
143 }
144 .asset-action-buy button:hover {
145 background: rgb(21, 61, 98);
146 }
147 .asset-link {
148 text-decoration: none;
149 color: #222222;
150 }
151 `
152 }
153
154 private static getAssetImageStyles(collection: OpenSeaCollection) {
155 // @ts-ignore - since card_display_style is not serialized by opensea sdk yet
156 const cardDisplayStyle = collection.displayData.card_display_style
157 return {
158 padding: cardDisplayStyle === 'padded' ? '10px' : '',
159 'background-size': `${cardDisplayStyle}`,
160 }
161 }
162
163 public getAssetPriceTemplate() {
164 const sellOrder =
165 this.asset?.sellOrders && this.asset?.sellOrders.length > 0
166 ? this.asset.sellOrders[0]
167 : null
168 const currentPriceTemplate =
169 sellOrder && sellOrder?.paymentTokenContract
170 ? this.getPriceTemplate(
171 PriceType.Current,
172 sellOrder?.paymentTokenContract,
173 sellOrder?.currentPrice?.toNumber() || 0
174 )
175 : null
176
177 const prevPriceTemplate = this.asset?.lastSale?.paymentToken
178 ? this.getPriceTemplate(
179 PriceType.Previous,
180 this.asset?.lastSale?.paymentToken,
181 +this.asset?.lastSale?.totalPrice
182 )
183 : null
184
185 return html`
186 <a class="asset-link" href="${this.asset?.openseaLink}" target="_blank">
187 ${currentPriceTemplate} ${prevPriceTemplate}
188 </a>
189 `
190 }
191
192 /**
193 * Implement `render` to define a template for your element.
194 */
195 public render() {
196 if (!this.asset) {
197 return undefined // If there is no asset then we can't render
198 }
199
200 const { openseaLink, collection, name } = this.asset
201 const { network } = this.state
202
203 return html`
204 <div class="card-front ${classMap({ 'is-vertical': !this.horizontal, 'is-flipped': this.flippedCard })}">
205 ${this.asset.traits.length > 0
206 ? html`
207 <info-button
208 style="position: absolute; top: 5px; right: 5px"
209 @flip-event="${(e: any) => this.eventHandler(e, 'flip')}"
210 ></info-button>
211 `
212 : ''}
213 ${this.getAssetImageTemplate()}
214
215 <div class="asset-details-container">
216 <div class="asset-detail">
217 <div class="asset-detail-type">
218 <a
219 class="asset-link"
220 href="http://${network === Network.Rinkeby
221 ? 'testnets.'
222 : ''}opensea.io/assets/${collection.slug}"
223 target="_blank"
224 >
225 <pill-element
226 .imageUrl=${collection.imageUrl}
227 .label=${collection.name}
228 textColor="#828282"
229 border="1px solid #E2E6EF"
230 ></pill-element>
231 </a>
232 </div>
233 <!-- This badge is optional and must be rendered programmatically -->
234 <!-- <div class="asset-detail-badge">
235 <pill-element
236 label="New"
237 backgroundColor="#23DC7D"
238 textColor="#FFFFFF"
239 ></pill-element>
240 </div> -->
241 </div>
242 <div class="spacer"></div>
243 <div class="asset-detail-name">
244 <a class="asset-link" href="${openseaLink}" target="_blank"
245 >${name}</a
246 >
247 </div>
248 ${this.getAssetPriceTemplate()}
249 <div class="asset-action-buy">${this.getButtonTemplate()}</div>
250 </div>
251 </div>
252 `
253 }
254
255 /*
256 * EventHandler - Dispatch event allowing parent to handle click event
257 * '_event' isn't used here but it's needed to call the handler
258 */
259 public eventHandler(_event: any, type: string) {
260 const buttonEvent = new CustomEvent('button-event', {
261 detail: {
262 type,
263 },
264 })
265 this.dispatchEvent(buttonEvent)
266 }
267
268 private getPriceTemplate(
269 priceType: PriceType,
270 paymentToken: OpenSeaFungibleToken,
271 price: number
272 ) {
273 return html`
274 <div class="asset-detail-price asset-detail-price-${priceType}">
275 ${priceType === PriceType.Previous
276 ? html` <div class="previous-value">Prev.&nbsp;</div> `
277 : null}
278 ${paymentToken.imageUrl
279 ? html`<img src="${paymentToken.imageUrl}" alt="" ></img>`
280 : html`
281 <div class="previous-value">
282 ${paymentToken.symbol === 'ETH' ? 'Ξ' : paymentToken.symbol}
283 </div>
284 `}
285 <div class="asset-detail-price-value">
286 ${toBaseDenomination(price, paymentToken.decimals)}
287 </div>
288 </div>
289 `
290 }
291
292 private getAssetImageTemplate() {
293 if (!this.asset) {
294 return undefined
295 }
296
297 const { openseaLink, imageUrl, collection } = this.asset
298 return html`
299 <div class="asset-image-container">
300 <a href="${openseaLink}" target="_blank">
301 <div
302 class="asset-image"
303 style=${styleMap({
304 'background-image': `url(${imageUrl})`,
305 ...NftCardFrontTemplate.getAssetImageStyles(collection),
306 })}
307 ></div>
308 </a>
309 </div>
310 `
311 }
312
313 private getButtonTemplate() {
314 return html`
315 <button @click="${(e: any) => this.eventHandler(e, 'view')}">
316 buy this item ❯
317 </button>
318 `
319 }
320
321}
322
\No newline at end of file