1 | import { css, customElement, html, LitElement, property } from 'lit-element'
|
2 |
|
3 | import { classMap } from 'lit-html/directives/class-map'
|
4 | import { styleMap } from 'lit-html/directives/style-map'
|
5 |
|
6 | import {
|
7 | Network,
|
8 | OpenSeaAsset,
|
9 | OpenSeaCollection,
|
10 | OpenSeaFungibleToken,
|
11 | } from 'opensea-js/lib/types'
|
12 |
|
13 | import './info-button'
|
14 | import { toBaseDenomination } from './utils'
|
15 | import { PriceType, State } from './types'
|
16 |
|
17 | @customElement('nft-card-front')
|
18 | export 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 |
|
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 |
|
194 |
|
195 | public render() {
|
196 | if (!this.asset) {
|
197 | return undefined
|
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. </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 |