From 099b248d698152bbcd4fa4551915e98f87824c3f Mon Sep 17 00:00:00 2001 From: Vaz-Tiago Date: Mon, 16 May 2022 09:26:42 -0300 Subject: [PATCH] loading spinner --- src/components.d.ts | 13 ++++++ src/components/side-drawer/side-drawer.tsx | 2 +- src/components/spinner/spinner.css | 46 ++++++++++++++++++++ src/components/spinner/spinner.tsx | 17 ++++++++ src/components/stock-finder/stock-finder.tsx | 20 ++++++--- src/components/stock-price/stock-price.tsx | 5 ++- 6 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 src/components/spinner/spinner.css create mode 100644 src/components/spinner/spinner.tsx diff --git a/src/components.d.ts b/src/components.d.ts index bb4e04e..fe1c7b1 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -11,6 +11,8 @@ export namespace Components { "opened": boolean; "titleMenu": string; } + interface VazSpinner { + } interface VazStockFinder { } interface VazStockPrice { @@ -29,6 +31,12 @@ declare global { prototype: HTMLVazSideDrawerElement; new (): HTMLVazSideDrawerElement; }; + interface HTMLVazSpinnerElement extends Components.VazSpinner, HTMLStencilElement { + } + var HTMLVazSpinnerElement: { + prototype: HTMLVazSpinnerElement; + new (): HTMLVazSpinnerElement; + }; interface HTMLVazStockFinderElement extends Components.VazStockFinder, HTMLStencilElement { } var HTMLVazStockFinderElement: { @@ -49,6 +57,7 @@ declare global { }; interface HTMLElementTagNameMap { "vaz-side-drawer": HTMLVazSideDrawerElement; + "vaz-spinner": HTMLVazSpinnerElement; "vaz-stock-finder": HTMLVazStockFinderElement; "vaz-stock-price": HTMLVazStockPriceElement; "vaz-tooltip": HTMLVazTooltipElement; @@ -59,6 +68,8 @@ declare namespace LocalJSX { "opened"?: boolean; "titleMenu"?: string; } + interface VazSpinner { + } interface VazStockFinder { "onVazSymbolSelect"?: (event: CustomEvent) => void; } @@ -71,6 +82,7 @@ declare namespace LocalJSX { } interface IntrinsicElements { "vaz-side-drawer": VazSideDrawer; + "vaz-spinner": VazSpinner; "vaz-stock-finder": VazStockFinder; "vaz-stock-price": VazStockPrice; "vaz-tooltip": VazTooltip; @@ -81,6 +93,7 @@ declare module "@stencil/core" { export namespace JSX { interface IntrinsicElements { "vaz-side-drawer": LocalJSX.VazSideDrawer & JSXBase.HTMLAttributes; + "vaz-spinner": LocalJSX.VazSpinner & JSXBase.HTMLAttributes; "vaz-stock-finder": LocalJSX.VazStockFinder & JSXBase.HTMLAttributes; "vaz-stock-price": LocalJSX.VazStockPrice & JSXBase.HTMLAttributes; "vaz-tooltip": LocalJSX.VazTooltip & JSXBase.HTMLAttributes; diff --git a/src/components/side-drawer/side-drawer.tsx b/src/components/side-drawer/side-drawer.tsx index 776d8fe..f305dae 100644 --- a/src/components/side-drawer/side-drawer.tsx +++ b/src/components/side-drawer/side-drawer.tsx @@ -20,7 +20,7 @@ export class SideDrawer { } @Method() - open() { + async open() { this.opened = true; } diff --git a/src/components/spinner/spinner.css b/src/components/spinner/spinner.css new file mode 100644 index 0000000..43f0a61 --- /dev/null +++ b/src/components/spinner/spinner.css @@ -0,0 +1,46 @@ +.lds-ripple { + display: inline-block; + position: relative; + width: 40px; + height: 40px; +} +.lds-ripple div { + position: absolute; + border: 4px solid #fff; + opacity: 1; + border-radius: 50%; + animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; +} +.lds-ripple div:nth-child(2) { + animation-delay: -0.5s; +} +@keyframes lds-ripple { + 0% { + top: 18px; + left: 18px; + width: 0; + height: 0; + opacity: 0; + } + 4.9% { + top: 18px; + left: 18px; + width: 0; + height: 0; + opacity: 0; + } + 5% { + top: 18px; + left: 18px; + width: 0; + height: 0; + opacity: 1; + } + 100% { + top: 0px; + left: 0px; + width: 36px; + height: 36px; + opacity: 0; + } +} diff --git a/src/components/spinner/spinner.tsx b/src/components/spinner/spinner.tsx new file mode 100644 index 0000000..eaf9a11 --- /dev/null +++ b/src/components/spinner/spinner.tsx @@ -0,0 +1,17 @@ +import { Component, h } from '@stencil/core'; + +@Component({ + tag: 'vaz-spinner', + styleUrl: './spinner.css', + shadow: true, +}) +export class Spinner { + render() { + return ( +
+
+
+
+ ); + } +} diff --git a/src/components/stock-finder/stock-finder.tsx b/src/components/stock-finder/stock-finder.tsx index cdced85..60363e8 100644 --- a/src/components/stock-finder/stock-finder.tsx +++ b/src/components/stock-finder/stock-finder.tsx @@ -9,11 +9,13 @@ export class StockFinder { stockNameInput: HTMLInputElement; @State() searchResults: { symbol: string; name: string }[] = []; + @State() loading = false; @Event({ bubbles: true, composed: true }) vazSymbolSelect: EventEmitter; async onFindStock(event: Event) { event.preventDefault(); + this.loading = true; const stockName = this.stockNameInput?.value; try { const apiResponse = await fetch(`https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${stockName}&apikey=${process.env.AV_TOKEN}`); @@ -24,6 +26,8 @@ export class StockFinder { })); } catch (err) { console.error('API ERROR: ', err); + } finally { + this.loading = false; } } @@ -32,18 +36,22 @@ export class StockFinder { } render() { - return [ -
- (this.stockNameInput = el)} /> - -
, + let content = (
    {this.searchResults.map((item, index) => (
  • {item.symbol} - {item.name}
  • ))} -
, + + ); + if (this.loading) content = ; + return [ +
+ (this.stockNameInput = el)} /> + +
, +
{content}
, ]; } } diff --git a/src/components/stock-price/stock-price.tsx b/src/components/stock-price/stock-price.tsx index fde8e6d..80aa286 100644 --- a/src/components/stock-price/stock-price.tsx +++ b/src/components/stock-price/stock-price.tsx @@ -49,7 +49,9 @@ export class StockPrice { try { const apiResponse = await fetch(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stockSymbol}&apikey=${process.env.AV_TOKEN}`); const parsedRes = await apiResponse.json(); + if (parsedRes?.Note) throw new Error('API limit, wait a minute to call again!'); if (!parsedRes['Global Quote']['05. price']) throw new Error('Invalid stock symbol'); + this.fetchedPrice = +parsedRes['Global Quote']['05. price']; this.error = null; } catch (err) { @@ -76,6 +78,7 @@ export class StockPrice { let dataContent =

Pleas enter a symbol

; if (this.error) dataContent =

{this.error}

; if (this.fetchedPrice) dataContent =

Price: ${this.fetchedPrice}

; + if (this.loading) dataContent = ; return [
(this.stockInput = el)} value={this.stockUserInput} onInput={this.onStockUserInput.bind(this)} /> @@ -83,7 +86,7 @@ export class StockPrice { Fetch
, -
{this.loading ?

Fetching Data...

: dataContent}
, +
{dataContent}
, ]; } }