Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: network selector component #70

Merged
merged 7 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/widget/src/assets/icons/chevron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { html } from 'lit';

const chevronIcon = html`<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M18 9.59998L12 15.6L6 9.59998"
stroke="#A1A1AA"
stroke-width="1.67"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg> `;

export default chevronIcon;
44 changes: 35 additions & 9 deletions packages/widget/src/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
export { default as switchNetworkIcon } from './icons/switchNetwork';
export { default as sygmaLogo } from './icons/sygmaLogo';
export { default as noNetworkIcon } from './icons/noNetworkIcon';
export { default as ethereumIcon } from './icons/ethereumNetworkIcon';
export { default as polygonNetworkIcon } from './icons/polygonNetworkIcon';
export { default as baseNetworkIcon } from './icons/baseNetworkIcon';
export { default as cronosNetworkIcon } from './icons/cronosNetworkIcon';
export { default as phalaNetworkIcon } from './icons/phalaNetworkIcon';
export { default as khalaNetworkIcon } from './icons/khalaNetworkIcon';
import type { HTMLTemplateResult } from 'lit';
import baseNetworkIcon from './icons/baseNetworkIcon';
import cronosNetworkIcon from './icons/cronosNetworkIcon';
import ethereumIcon from './icons/ethereumNetworkIcon';
import khalaNetworkIcon from './icons/khalaNetworkIcon';
import noNetworkIcon from './icons/noNetworkIcon';
import phalaNetworkIcon from './icons/phalaNetworkIcon';
import polygonNetworkIcon from './icons/polygonNetworkIcon';
import switchNetworkIcon from './icons/switchNetwork';
import sygmaLogo from './icons/sygmaLogo';
import chevronIcon from './icons/chevron';

export const networkIconsMap = {
ethereum: ethereumIcon,
khala: khalaNetworkIcon,
phala: phalaNetworkIcon,
cronos: cronosNetworkIcon,
base: baseNetworkIcon,
gnosis: noNetworkIcon,
polygon: polygonNetworkIcon,
default: noNetworkIcon
} as const as Record<string, HTMLTemplateResult>;

export {
ethereumIcon,
khalaNetworkIcon,
phalaNetworkIcon,
cronosNetworkIcon,
baseNetworkIcon,
polygonNetworkIcon,
sygmaLogo,
switchNetworkIcon,
noNetworkIcon,
chevronIcon
};
114 changes: 68 additions & 46 deletions packages/widget/src/components/network-selector/network-selector.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { Domain } from '@buildwithsygma/sygma-sdk-core';
import { LitElement, html, type HTMLTemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { customElement, property, state } from 'lit/decorators.js';
import { map } from 'lit/directives/map.js';
import { capitalize } from '../../utils';
import { networkIconsMap, chevronIcon } from '../../assets';
import { styles } from './styles';

export const Directions = {
Expand All @@ -16,70 +17,91 @@ type Direction = (typeof Directions)[keyof typeof Directions];
export class NetworkSelector extends LitElement {
static styles = styles;

@property({
type: Boolean
})
disabled = false;
@state()
_isDropdownOpen = false;

@property({
type: Boolean
})
icons = true;
@property({ type: Boolean })
disabled = false;

@property({
type: String
})
@property({ type: String })
direction?: Direction;
mpetrunic marked this conversation as resolved.
Show resolved Hide resolved

@property({
type: Object
})
selected?: Domain;
@property({ type: Object })
selectedNetwork?: Domain;

@property({
attribute: false
})
@property({ attribute: false })
onNetworkSelected?: (network?: Domain) => void;
mpetrunic marked this conversation as resolved.
Show resolved Hide resolved

@property({
type: Array
})
@property({ type: Array })
networks: Domain[] = [];

onChange({ target }: Event): void {
const { value } = target as HTMLOptionElement;
const network = this.networks.find((n) => String(n.chainId) == value);
this.onNetworkSelected?.(network);
_toggleDropdown = (): void => {
this._isDropdownOpen = !this._isDropdownOpen;
};

_selectOption(option: Domain, event: Event): void {
event.stopPropagation();
this.selectedNetwork = option;
this._isDropdownOpen = false;
Lykhoyda marked this conversation as resolved.
Show resolved Hide resolved
this.onNetworkSelected?.(option);
}

renderEntries(): Generator<unknown, void> | HTMLTemplateResult {
if (this.networks) {
return map(this.networks, (entry: Domain) => {
// TODO: render network icon
return html`<option value=${entry.chainId} class="network-option">
${capitalize(entry.name)}
</option>`;
});
}
return html`<option selected value="">Network</option>`;
_renderNetworkIcon(name: string): HTMLTemplateResult {
return networkIconsMap[name] || networkIconsMap.default;
}

_renderEntries(): Generator<unknown, void> | HTMLTemplateResult {
mpetrunic marked this conversation as resolved.
Show resolved Hide resolved
return map(
this.networks,
(network: Domain) => html`
<div
class="dropdownOption"
@click="${(e: Event) => this._selectOption(network, e)}"
role="option"
>
${this._renderNetworkIcon(network.name)}
<span class="networkName">${capitalize(network.name)}</span>
</div>
`
);
}

_renderTriggerContent(): HTMLTemplateResult {
return this.selectedNetwork
mpetrunic marked this conversation as resolved.
Show resolved Hide resolved
? html`${this._renderNetworkIcon(this.selectedNetwork.name)}
<span class="networkName">
${capitalize(this.selectedNetwork.name)}
</span>`
: html`Select Network`;
}

render(): HTMLTemplateResult {
return html` <div class="selectorContainer">
return html`<div class="selectorContainer">
<label for="selector" class="directionLabel">${this.direction}</label>
<section class="selectorSection">
<select
@change=${(event: Event) => this.onChange.bind(this)(event)}
?disabled=${this.disabled}
class="selector"
<div
class="dropdown"
@click="${this._toggleDropdown}"
role="listbox"
tabindex="0"
aria-expanded="${this._isDropdownOpen ? 'true' : 'false'}"
>
<div class="dropdownTrigger">
<div class="selectedNetwork">${this._renderTriggerContent()}</div>
<div class="chevron ${this._isDropdownOpen ? 'open' : ''}">
${chevronIcon}
</div>
</div>
<div
class="dropdownContent ${this._isDropdownOpen ? 'show' : ''}"
mpetrunic marked this conversation as resolved.
Show resolved Hide resolved
role="list"
>
<option class="network-option" value="-1">-</option>
${this.renderEntries()}
</select>
</section>
${this._renderEntries()}
</div>
</div>
</div>`;
}
}

declare global {
interface HTMLElementTagNameMap {
'sygma-network-selector': NetworkSelector;
Expand Down
96 changes: 86 additions & 10 deletions packages/widget/src/components/network-selector/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ import { css } from 'lit';

export const styles = css`
.selectorContainer {
border-radius: 24px;
border: 1px solid #e4e4e7;
border-radius: 1.5rem;
border: 0.0625rem solid #e4e4e7;
display: flex;
width: 314px;
padding: 12px 16px;
width: 100%;
max-width: 19.625rem;
max-height: 4.625rem;
padding: 0.75rem 1rem;
flex-direction: column;
justify-content: center;
align-items: stretch;
gap: 4px;
gap: 0.25rem;
}

.directionLabel {
color: #a1a1aa;
font-size: 14px;
font-size: 0.875rem;
font-weight: 500;
line-height: 20px;
line-height: 1.25rem;
display: flex;
flex-direction: column;
justify-content: center;
Expand All @@ -30,15 +32,89 @@ export const styles = css`
.selector {
width: 100%;
color: #525252;
font-size: 18px;
font-size: 1.125rem;
font-weight: 500;
line-height: 26px;
line-height: 1.625rem;
border: none;
}

.selectorSection {
display: flex;
align-items: center;
gap: 12px;
gap: 0.75rem;
}

.dropdown {
position: relative;
width: 100%;
}

.selectedNetwork {
display: flex;
align-items: center;
}

.dropdownTrigger {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
padding: 0.25rem 0;
box-sizing: border-box;
}

.chevron {
transform: rotate(0deg);
transition: transform 0.3s ease;

&.open {
transform: rotate(180deg);
}
}

.dropdownContent {
display: none;
position: absolute;
background-color: white;
width: 100%;
border-radius: 0.75rem;
border: 0.0625rem solid #f3f4f6;
box-shadow:
0 0.25rem 0.375rem -0.0625rem rgba(0, 0, 0, 0.1),
0 0.125rem 0.25rem -0.0625rem rgba(0, 0, 0, 0.06);
z-index: 1;
margin-top: 1rem;
}

.show {
display: block;
}

.dropdownOption {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
cursor: pointer;
transition: background-color 0.3s ease;

svg {
max-width: 1.43656rem;
width: 100%;
}

&:hover {
background-color: #e9e4dd;
}
}

.networkIcon {
display: block;
width: 1.25rem;
height: 1.25rem;
}

.networkName {
margin-left: 0.5rem;
}
`;
4 changes: 4 additions & 0 deletions packages/widget/src/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export const styles = css`
font-family: Inter, sans-serif;
}

.networkSelectionWrapper {
margin: 1rem 0 0.5rem 0;
}

.connectAccount {
display: flex;
justify-content: flex-end;
Expand Down
2 changes: 1 addition & 1 deletion packages/widget/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class SygmaProtocolWidget extends LitElement {
<section class="connectAccount">
${switchNetworkIcon} Connect Wallet
</section>
<section>
<section class="networkSelectionWrapper">
<sygma-network-selector
.direction=${Directions.FROM}
.icons=${true}
Expand Down
Loading
Loading