adding UI for user orders

This commit is contained in:
sairaj mote 2021-10-07 22:47:01 +05:30
parent d1bb6e6d2e
commit d6df6e523c
5 changed files with 1204 additions and 126 deletions

View File

@ -2247,8 +2247,9 @@ smSelect.innerHTML = `
-webkit-tap-highlight-color: transparent;
}
.icon {
height: 1.5rem;
width: 1.5rem;
height: 1.2rem;
width: 1.2rem;
margin-left: 0.5rem;
fill: rgba(var(--text-color), 0.7);
}
.selected-option-text{
@ -2265,7 +2266,7 @@ smSelect.innerHTML = `
-ms-grid-columns: 1fr auto;
grid-template-columns: 1fr auto;
grid-template-areas: 'heading heading' '. .';
padding: 0.4rem 1rem;
padding: 0.4rem 0.8rem;
background: rgba(var(--text-color), 0.06);
border: solid 1px rgba(var(--text-color), 0.2);
-webkit-box-align: center;
@ -2277,9 +2278,6 @@ smSelect.innerHTML = `
-webkit-box-shadow: 0 0 0 0.1rem var(--accent-color);
box-shadow: 0 0 0 0.1rem var(--accent-color)
}
.icon{
margin-left: 1rem;
}
:host([align-select="left"]) .options{
left: 0;
}
@ -2558,12 +2556,13 @@ smOption.innerHTML = `
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
min-width: 100%;
min-width: max-content;
width: 100%;
gap: 0.5rem;
grid-template-columns: max-content minmax(0, 1fr);
padding: 0.8rem 1.2rem;
cursor: pointer;
overflow-wrap: break-word;
white-space: nowrap;
outline: none;
user-select: none;
}
@ -2609,3 +2608,584 @@ customElements.define('sm-option', class extends HTMLElement {
this.setAttribute('tabindex', '0')
}
})
const smCheckbox = document.createElement('template')
smCheckbox.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
:host{
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
--accent-color: #4d2588;
--text-color: 17, 17, 17;
--background-color: 255, 255, 255;
--height: 1.2rem;
--width: 1.2rem;
--border-radius: 0.2rem;
--border-color: rgba(var(--text-color), 0.7);
}
:host([disabled]) {
opacity: 0.6;
user-select: none;
pointer-events: none;
}
.checkbox {
position: relative;
display:-webkit-box;
display:-ms-flexbox;
display:flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
}
.checkbox:focus-visible{
outline: auto;
}
.checkbox:active .icon,
.checkbox:focus-within .icon{
box-shadow: 0 0 0 0.1rem var(--accent-color) inset;
}
input {
display: none;
}
.checkmark {
stroke-dashoffset: -65;
stroke-dasharray: 65;
-webkit-transition: stroke-dashoffset 0.3s;
-o-transition: stroke-dashoffset 0.3s;
transition: stroke-dashoffset 0.3s;
}
:host([checked]) .checkmark {
stroke-dashoffset: 0;
stroke: rgba(var(--background-color), 1);
}
:host([checked]) .icon {
background: var(--accent-color);
box-shadow: 0 0 0 0.1rem var(--accent-color) inset;
}
.icon {
fill: none;
height: var(--height);
width: var(--width);
padding: 0.1rem;
stroke-width: 8;
stroke: var(--border-color);
overflow: visible;
stroke-linecap: round;
stroke-linejoin: round;
-webkit-transition: background 0.3s;
-o-transition: background 0.3s;
transition: background 0.3s;
border-radius: var(--border-radius);
box-shadow: 0 0 0 0.1rem var(--border-color) inset;
}
</style>
<label class="checkbox">
<svg class="icon" viewBox="0 0 64 64">
<path class="checkmark" d="M50.52,19.56,26,44.08,13.48,31.56" />
</svg>
<slot></slot>
</label>`
customElements.define('sm-checkbox', class extends HTMLElement {
constructor() {
super()
this.attachShadow({
mode: 'open'
}).append(smCheckbox.content.cloneNode(true))
this.checkbox = this.shadowRoot.querySelector('.checkbox');
this.reset = this.reset.bind(this)
this.dispatch = this.dispatch.bind(this)
this.handleKeyDown = this.handleKeyDown.bind(this)
this.handleClick = this.handleClick.bind(this)
}
static get observedAttributes() {
return ['value', 'disabled', 'checked']
}
get disabled() {
return this.hasAttribute('disabled')
}
set disabled(val) {
if (val) {
this.setAttribute('disabled', '')
} else {
this.removeAttribute('disabled')
}
}
get checked() {
return this.hasAttribute('checked')
}
set checked(value) {
if (value) {
this.setAttribute('checked', '')
}
else {
this.removeAttribute('checked')
}
}
set value(val) {
this.setAttribute('value', val)
}
get value() {
return this.getAttribute('value')
}
reset() {
this.removeAttribute('checked')
}
dispatch() {
this.dispatchEvent(new CustomEvent('change', {
bubbles: true,
composed: true
}))
}
handleKeyDown(e) {
if (e.code === "Space") {
e.preventDefault()
this.click()
}
}
handleClick(e) {
this.toggleAttribute('checked')
}
connectedCallback() {
if (!this.hasAttribute('disabled')) {
this.setAttribute('tabindex', '0')
}
this.setAttribute('role', 'checkbox')
if (!this.hasAttribute('checked')) {
this.setAttribute('aria-checked', 'false')
}
this.addEventListener('keydown', this.handleKeyDown)
this.addEventListener('click', this.handleClick)
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
if (name === 'checked') {
this.setAttribute('aria-checked', this.hasAttribute('checked'))
this.dispatch()
}
else if (name === 'disabled') {
if (this.hasAttribute('disabled')) {
this.removeAttribute('tabindex')
}
else {
this.setAttribute('tabindex', '0')
}
}
}
}
disconnectedCallback() {
this.removeEventListener('keydown', this.handleKeyDown)
this.removeEventListener('change', this.handleClick)
}
})
const smTabHeader = document.createElement('template')
smTabHeader.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
:host{
display: -webkit-box;
display: -ms-flexbox;
display: flex;
--accent-color: #4d2588;
--text-color: 17, 17, 17;
--background-color: 255, 255, 255;
--gap: 1rem;
--justify-content: flex-start;
--tab-indicator-border-radius: 0.3rem;
}
.tabs{
position: relative;
display: -ms-grid;
display: grid;
width: 100%;
}
.tab-header{
display: -ms-grid;
display: grid;
grid-auto-flow: column;
justify-content: var(--justify-content);
gap: var(--gap);
position: relative;
overflow: auto hidden;
max-width: 100%;
scrollbar-width: 0;
}
.indicator{
position: absolute;
left: 0;
bottom: 0;
height: 0.15rem;
border-radius: 1rem 1rem 0 0;
background: var(--accent-color);
-webkit-transition: width 0.3s, -webkit-transform 0.3s;
transition: width 0.3s, -webkit-transform 0.3s;
-o-transition: transform 0.3s, width 0.3s;
transition: transform 0.3s, width 0.3s;
transition: transform 0.3s, width 0.3s, -webkit-transform 0.3s;
pointer-events: none;
}
:host([variant="tab"]) .indicator{
height: 100%;
border-radius: var(--tab-indicator-border-radius);
}
:host([variant="tab"]) .tab-header{
border-bottom: none;
}
.hide-completely{
display: none;
}
:host([variant="tab"]) .tab-header{
gap: 0.2rem;
display: -ms-inline-grid;
display: inline-grid;
justify-self: flex-start;
border-radius: 0.3rem;
}
:host([variant="tab"]) slot::slotted(.active){
color: rgba(var(--background-color), 1);
}
slot::slotted(.active){
color: var(--accent-color);
opacity: 1;
}
@media (any-hover: none){
.tab-header::-webkit-scrollbar-track {
-webkit-box-shadow: none !important;
background-color: transparent !important;
}
.tab-header::-webkit-scrollbar {
height: 0;
background-color: transparent;
}
}
@media (any-hover: hover){
.tab-header{
overflow: hidden;
}
}
</style>
<div part="tab-container" class="tabs">
<div part="tab-header" class="tab-header">
<slot></slot>
<div part="indicator" class="indicator"></div>
</div>
</div>
`;
customElements.define('sm-tab-header', class extends HTMLElement {
constructor() {
super()
this.attachShadow({
mode: 'open'
}).append(smTabHeader.content.cloneNode(true))
this.prevTab
this.allTabs
this.activeTab
this.indicator = this.shadowRoot.querySelector('.indicator');
this.tabSlot = this.shadowRoot.querySelector('slot');
this.tabHeader = this.shadowRoot.querySelector('.tab-header');
this.changeTab = this.changeTab.bind(this)
this.handleClick = this.handleClick.bind(this)
this.handlePanelChange = this.handlePanelChange.bind(this)
this.moveIndiactor = this.moveIndiactor.bind(this)
}
fireEvent(index) {
this.dispatchEvent(
new CustomEvent(`switchedtab${this.target}`, {
bubbles: true,
detail: {
index: parseInt(index)
}
})
)
}
moveIndiactor(tabDimensions) {
this.indicator.setAttribute('style', `width: ${tabDimensions.width}px; transform: translateX(${tabDimensions.left - this.tabHeader.getBoundingClientRect().left + this.tabHeader.scrollLeft}px)`)
}
changeTab(target) {
if (target === this.prevTab || !target.closest('sm-tab'))
return
if (this.prevTab)
this.prevTab.classList.remove('active')
target.classList.add('active')
this.tabHeader.scrollTo({
behavior: 'smooth',
left: target.getBoundingClientRect().left - this.tabHeader.getBoundingClientRect().left + this.tabHeader.scrollLeft
})
this.moveIndiactor(target.getBoundingClientRect())
this.prevTab = target;
this.activeTab = target;
}
handleClick(e) {
if (e.target.closest('sm-tab')) {
this.changeTab(e.target)
this.fireEvent(e.target.dataset.index)
}
}
handlePanelChange(e) {
this.changeTab(this.allTabs[e.detail.index])
}
connectedCallback() {
if (!this.hasAttribute('target') || this.getAttribute('target').value === '') return;
this.target = this.getAttribute('target')
this.tabSlot.addEventListener('slotchange', () => {
this.allTabs = this.tabSlot.assignedElements();
this.allTabs.forEach((tab, index) => {
tab.dataset.index = index
})
})
this.addEventListener('click', this.handleClick)
document.addEventListener(`switchedpanel${this.target}`, this.handlePanelChange)
let resizeObserver = new ResizeObserver(entries => {
entries.forEach((entry) => {
if (this.prevTab) {
let tabDimensions = this.activeTab.getBoundingClientRect();
this.moveIndiactor(tabDimensions)
}
})
})
resizeObserver.observe(this)
let observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.indicator.style.transition = 'none'
if (this.activeTab) {
let tabDimensions = this.activeTab.getBoundingClientRect();
this.moveIndiactor(tabDimensions)
} else {
this.allTabs[0].classList.add('active')
let tabDimensions = this.allTabs[0].getBoundingClientRect();
this.moveIndiactor(tabDimensions)
this.fireEvent(0)
this.prevTab = this.tabSlot.assignedElements()[0];
this.activeTab = this.prevTab;
}
}
})
}, {
threshold: 1.0
})
observer.observe(this)
}
disconnectedCallback() {
this.removeEventListener('click', this.handleClick)
document.removeEventListener(`switchedpanel${this.target}`, this.handlePanelChange)
}
})
// tab
const smTab = document.createElement('template')
smTab.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
:host{
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
z-index: 1;
--padding: 0.8rem 1rem;
}
.tab{
position: relative;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
white-space: nowrap;
padding: var(--padding);
font-weight: 500;
word-spacing: 0.1rem;
text-align: center;
-webkit-transition: color 0.3s;
-o-transition: color 0.3s;
transition: color 0.3s;
text-transform: capitalize;
height: 100%;
}
@media (hover: hover){
:host(.active) .tab{
opacity: 1;
}
.tab{
opacity: 0.7
}
.tab:hover{
opacity: 1
}
}
</style>
<div part="tab" class="tab">
<slot></slot>
</div>
`;
customElements.define('sm-tab', class extends HTMLElement {
constructor() {
super()
this.shadow = this.attachShadow({
mode: 'open'
}).append(smTab.content.cloneNode(true))
}
})
// tab-panels
const smTabPanels = document.createElement('template')
smTabPanels.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
:host{
width: 100%;
}
.panel-container{
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
width: 100%;
height: 100%;
overflow: hidden;
scroll-snap-type: x mandatory;
content-visibility: auto;
}
::slotted(*){
min-width: 100%;
scroll-snap-align: center;
}
@media (any-hover: none) {
.panel-container{
overflow-x: auto;
scrollbar-width: none;
}
.container {
overflow-y: scroll;
}
::-webkit-scrollbar {
width: 0;
height: 0;
}
}
</style>
<div part="panel-container" class="panel-container">
<slot>Nothing to see here.</slot>
</div>
`;
customElements.define('sm-tab-panels', class extends HTMLElement {
constructor() {
super()
this.attachShadow({
mode: 'open'
}).append(smTabPanels.content.cloneNode(true))
this.isTransitioning = false
this.panelContainer = this.shadowRoot.querySelector('.panel-container');
this.handleTabChange = this.handleTabChange.bind(this)
}
handleTabChange(e) {
this.isTransitioning = true
this.panelContainer.scrollTo({
left: this.allPanels[e.detail.index].getBoundingClientRect().left - this.panelContainer.getBoundingClientRect().left + this.panelContainer.scrollLeft,
behavior: 'smooth'
})
setTimeout(() => {
this.isTransitioning = false
}, 300);
}
fireEvent(index) {
this.dispatchEvent(
new CustomEvent(`switchedpanel${this.id}`, {
bubbles: true,
detail: {
index: parseInt(index)
}
})
)
}
connectedCallback() {
const slot = this.shadowRoot.querySelector('slot');
slot.addEventListener('slotchange', (e) => {
this.allPanels = e.target.assignedElements()
this.allPanels.forEach((panel, index) => {
panel.dataset.index = index
intersectionObserver.observe(panel)
})
})
document.addEventListener(`switchedtab${this.id}`, this.handleTabChange)
const intersectionObserver = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (!this.isTransitioning && entry.isIntersecting) {
this.fireEvent(entry.target.dataset.index)
}
})
}, {
threshold: 0.6
})
}
disconnectedCallback() {
intersectionObserver.disconnect()
document.removeEventListener(`switchedtab${this.id}`, this.handleTabChange)
}
})

View File

@ -23,7 +23,7 @@ body {
body,
body * {
--accent-color: #504dff;
--accent-color--light: #f4f4ff;
--accent-color--light: #eeeeff;
--text-color: 36, 36, 36;
--background-color: 255, 255, 255;
--foreground-color: rgb(250, 252, 255);
@ -93,16 +93,19 @@ button,
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
-webkit-tap-highlight-color: transparent;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 0.9rem;
font-weight: 500;
}
.button {
white-space: nowrap;
padding: 0.6rem 1rem;
border-radius: 0.3rem;
font-weight: 500;
font-size: 0.8rem;
background-color: var(--accent-color--light);
color: var(--accent-color);
background-color: rgba(var(--text-color), 0.06);
color: rgba(var(--text-color), 0.8);
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
@ -319,13 +322,7 @@ ul {
justify-content: space-between;
}
.stretch {
-webkit-box-pack: stretch;
-ms-flex-pack: stretch;
justify-content: stretch;
justify-items: stretch;
}
.stretch > * {
.w-100 {
width: 100%;
}
@ -484,9 +481,7 @@ strip-select {
}
strip-option {
text-transform: uppercase;
font-weight: 500;
letter-spacing: 0.05em;
font-size: 0.8rem;
--border-radius: 0;
--active-option-color: rgba(var(--background-color), 1);
@ -499,6 +494,11 @@ strip-option:last-of-type {
--border-radius: 0 0.3rem 0.3rem 0;
}
sm-select,
sm-option {
font-size: 0.9rem;
}
.warning {
background-color: khaki;
color: rgba(0, 0, 0, 0.7);
@ -519,15 +519,6 @@ strip-option:last-of-type {
height: 100%;
}
.table__row {
display: grid;
grid-template-columns: repeat(var(--table-columns), auto);
}
.table__header {
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
}
#landing {
grid-template-rows: auto 1fr;
}
@ -632,7 +623,100 @@ strip-option:last-of-type {
}
#orders_section {
padding: 1.5rem;
padding: 1.5rem 0;
}
#orders_section .icon {
height: 1.2rem;
width: 1.2rem;
}
#orders_section__header {
min-height: 2.8rem;
padding: 0 1.5rem;
}
#orders_section__header--primary {
-ms-flex-wrap: wrap;
flex-wrap: wrap;
z-index: 1;
}
#orders_section__header--primary sm-tab-header {
--gap: 1.5rem;
}
#orders_section__header--primary sm-tab {
font-size: 0.9rem;
--padding: 0.8rem 0;
}
.table {
display: grid;
}
.table__header {
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
}
.table__row {
padding: 0.5rem 1.5rem;
display: grid;
grid-template-columns: -webkit-min-content repeat(3, 1fr) -webkit-min-content;
grid-template-columns: min-content repeat(3, 1fr) min-content;
}
.order-card {
position: relative;
outline: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1.5rem 0.5rem 0.5rem;
}
.order-card[data-type=buy] .order-card__type, .order-card[data-type=sell] .order-card__type {
font-size: 0.9rem;
font-weight: 500;
margin-bottom: 0.3rem;
}
.order-card[data-type=buy] .order-card__type {
color: var(--green);
}
.order-card[data-type=sell] .order-card__type {
color: var(--danger-color);
}
.order-card--selected {
background-color: rgba(var(--text-color), 0.08);
}
.order-card--selected .cancel-order {
visibility: hidden;
pointer-events: none;
}
.order-card sm-checkbox {
--height: 1rem;
--width: 1rem;
padding: 1rem;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.order-card__quantity, .order-card__price {
font-size: 0.9rem;
color: rgba(var(--text-color), 0.9);
}
.order-card__time {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
}
.cancel-order {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
padding: 0.4rem 0.6rem;
}
.cancel-order span {
margin-left: 0.3rem;
}
#user_section {
@ -653,6 +737,12 @@ strip-option:last-of-type {
flex: 1;
}
.label {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
margin-bottom: 0.2rem;
}
.balance-card {
display: grid;
-webkit-box-align: center;
@ -666,10 +756,6 @@ strip-option:last-of-type {
grid-template-columns: auto 1fr;
gap: 1rem;
}
.balance-card.is-locked .label {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
}
.balance-card:not(.is-locked) {
grid-template-columns: auto 1fr auto;
}
@ -774,18 +860,12 @@ strip-option:last-of-type {
.page-layout {
grid-template-columns: 1fr 90vw 1fr;
}
}
@media screen and (min-width: 64rem) {
.page-layout {
grid-template-columns: 1fr 80vw 1fr;
}
#home {
-webkit-box-align: start;
-ms-flex-align: start;
align-items: flex-start;
padding: 1.5vmax 3vmax;
grid-template-columns: 24rem minmax(0, 1fr) 20rem;
padding: 1.5vmax;
gap: 1rem;
}
#home > * {
@ -798,6 +878,26 @@ strip-option:last-of-type {
display: none;
}
}
@media screen and (min-width: 48rem) {
#home {
grid-template-rows: -webkit-min-content 1fr;
grid-template-rows: min-content 1fr;
grid-template-columns: 24rem minmax(0, 1fr);
}
#orders_section {
grid-row: span 2;
}
}
@media screen and (min-width: 72rem) {
.page-layout {
grid-template-columns: 1fr 80vw 1fr;
}
#home {
grid-template-columns: 24rem minmax(0, 1fr) 20rem;
}
}
@media screen and (min-width: 120rem) {
.page-layout {
grid-template-columns: 1fr 70vw 1fr;
@ -817,15 +917,44 @@ strip-option:last-of-type {
background: rgba(var(--text-color), 0.5);
}
.nav-item,
.interact {
.interact {
-webkit-transition: background-color 0.3s, -webkit-transform 0.3s;
transition: background-color 0.3s, -webkit-transform 0.3s;
transition: background-color 0.3s, transform 0.3s;
transition: background-color 0.3s, transform 0.3s, -webkit-transform 0.3s;
}
.nav-item:hover,
.interact:hover {
.interact:hover {
background-color: var(--accent-color--light);
}
.order-card .cancel-order {
justify-self: flex-end;
overflow: hidden;
}
.order-card .cancel-order .icon,
.order-card .cancel-order span {
-webkit-transition: opacity 0.3s, -webkit-transform 0.3s;
transition: opacity 0.3s, -webkit-transform 0.3s;
transition: opacity 0.3s, transform 0.3s;
transition: opacity 0.3s, transform 0.3s, -webkit-transform 0.3s;
}
.order-card .cancel-order .icon {
opacity: 0;
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
.order-card .cancel-order span {
-webkit-transform: translateX(100%);
transform: translateX(100%);
opacity: 0;
}
.order-card:hover .cancel-order .icon, .order-card:focus-within .cancel-order .icon {
opacity: 1;
}
.order-card .cancel-order:hover .icon,
.order-card .cancel-order:hover span {
opacity: 1;
-webkit-transform: translateX(0);
transform: translateX(0);
}
}

File diff suppressed because one or more lines are too long

View File

@ -19,7 +19,7 @@ body {
&,
* {
--accent-color: #504dff;
--accent-color--light: #f4f4ff;
--accent-color--light: #eeeeff;
--text-color: 36, 36, 36;
--background-color: 255, 255, 255;
--foreground-color: rgb(250, 252, 255);
@ -87,15 +87,16 @@ button,
cursor: pointer;
transition: transform 0.3s;
-webkit-tap-highlight-color: transparent;
align-items: center;
font-size: 0.9rem;
font-weight: 500;
}
.button {
white-space: nowrap;
padding: 0.6rem 1rem;
border-radius: 0.3rem;
font-weight: 500;
font-size: 0.8rem;
background-color: var(--accent-color--light);
color: var(--accent-color);
background-color: rgba(var(--text-color), 0.06);
color: rgba(var(--text-color), 0.8);
justify-content: center;
&--primary {
background-color: var(--accent-color);
@ -129,7 +130,6 @@ sm-button {
}
}
}
ul {
list-style: none;
}
@ -289,13 +289,8 @@ ul {
justify-content: space-between;
}
.stretch {
justify-content: stretch;
justify-items: stretch;
& > * {
width: 100%;
}
.w-100 {
width: 100%;
}
.interact {
@ -430,9 +425,7 @@ strip-select {
border-radius: 0.3rem;
}
strip-option {
text-transform: uppercase;
font-weight: 500;
letter-spacing: 0.05em;
font-size: 0.8rem;
--border-radius: 0;
--active-option-color: rgba(var(--background-color), 1);
@ -444,6 +437,10 @@ strip-option {
--border-radius: 0 0.3rem 0.3rem 0;
}
}
sm-select,
sm-option {
font-size: 0.9rem;
}
.warning {
background-color: khaki;
color: rgba(0, 0, 0, 0.7);
@ -461,16 +458,6 @@ strip-option {
.page {
height: 100%;
}
.table {
&__row {
display: grid;
grid-template-columns: repeat(var(--table-columns), auto);
}
&__header {
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
}
}
#landing {
grid-template-rows: auto 1fr;
header {
@ -562,8 +549,94 @@ strip-option {
min-width: 8ch;
}
#orders_section {
padding: 1.5rem;
padding: 1.5rem 0;
.icon {
height: 1.2rem;
width: 1.2rem;
}
}
#orders_section__header {
min-height: 2.8rem;
padding: 0 1.5rem;
}
#orders_section__header--primary {
flex-wrap: wrap;
z-index: 1;
sm-tab-header {
--gap: 1.5rem;
}
sm-tab {
font-size: 0.9rem;
--padding: 0.8rem 0;
}
}
.table {
display: grid;
&__header {
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
}
&__row {
padding: 0.5rem 1.5rem;
display: grid;
grid-template-columns: min-content repeat(3, 1fr) min-content;
}
}
.order-card {
position: relative;
outline: none;
user-select: none;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1.5rem 0.5rem 0.5rem;
&[data-type="buy"] &__type,
&[data-type="sell"] &__type {
font-size: 0.9rem;
font-weight: 500;
margin-bottom: 0.3rem;
}
&[data-type="buy"] &__type {
color: var(--green);
}
&[data-type="sell"] &__type {
color: var(--danger-color);
}
&--selected {
background-color: rgba(var(--text-color), 0.08);
.cancel-order {
visibility: hidden;
pointer-events: none;
}
}
sm-checkbox {
--height: 1rem;
--width: 1rem;
padding: 1rem;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
&__quantity,
&__price {
font-size: 0.9rem;
color: rgba(var(--text-color), 0.9);
}
&__time {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
}
}
.cancel-order {
align-items: center;
padding: 0.4rem 0.6rem;
span {
margin-left: 0.3rem;
}
}
#user_section {
gap: 1.5rem;
padding: 1.5rem;
@ -579,6 +652,11 @@ strip-option {
flex: 1;
}
}
.label {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
margin-bottom: 0.2rem;
}
.balance-card {
display: grid;
align-items: center;
@ -588,10 +666,6 @@ strip-option {
&.is-locked {
grid-template-columns: auto 1fr;
gap: 1rem;
.label {
font-size: 0.8rem;
color: rgba(var(--text-color), 0.8);
}
}
&:not(.is-locked) {
grid-template-columns: auto 1fr auto;
@ -675,17 +749,9 @@ strip-option {
.page-layout {
grid-template-columns: 1fr 90vw 1fr;
}
}
@media screen and (max-width: 64rem) {
}
@media screen and (min-width: 64rem) {
.page-layout {
grid-template-columns: 1fr 80vw 1fr;
}
#home {
align-items: flex-start;
padding: 1.5vmax 3vmax;
grid-template-columns: 24rem minmax(0, 1fr) 20rem;
padding: 1.5vmax;
gap: 1rem;
& > * {
border-radius: 0.5rem;
@ -697,6 +763,25 @@ strip-option {
display: none;
}
}
@media screen and (min-width: 48rem) {
#home {
grid-template-rows: min-content 1fr;
grid-template-columns: 24rem minmax(0, 1fr);
}
#orders_section {
grid-row: span 2;
}
}
@media screen and (max-width: 64rem) {
}
@media screen and (min-width: 72rem) {
.page-layout {
grid-template-columns: 1fr 80vw 1fr;
}
#home {
grid-template-columns: 24rem minmax(0, 1fr) 20rem;
}
}
@media screen and (min-width: 120rem) {
.page-layout {
grid-template-columns: 1fr 70vw 1fr;
@ -716,11 +801,43 @@ strip-option {
background: rgba(var(--text-color), 0.5);
}
}
.nav-item,
.interact {
transition: background-color 0.3s, transform 0.3s;
&:hover {
background-color: var(--accent-color--light);
}
}
.order-card {
.cancel-order {
justify-self: flex-end;
overflow: hidden;
.icon,
span {
transition: opacity 0.3s, transform 0.3s;
}
.icon {
opacity: 0;
transform: translateX(100%);
}
span {
transform: translateX(100%);
opacity: 0;
}
}
&:hover,
&:focus-within {
.cancel-order {
.icon {
opacity: 1;
}
}
}
.cancel-order:hover {
.icon,
span {
opacity: 1;
transform: translateX(0);
}
}
}
}

View File

@ -165,7 +165,7 @@
<sm-form id="trade_form">
<div class="flex space-between align-center">
<h4>Trade FLO</h4>
<strip-select id="trade_type_selector">
<strip-select id="trade_type_selector" class="tab">
<strip-option value="buy" selected>Buy</strip-option>
<strip-option value="sell">Sell</strip-option>
</strip-select>
@ -187,21 +187,56 @@
</sm-form>
</section>
<section id="orders_section" class="grid gap-1-5">
<div class="flex space-between align-center">
<h4>My orders</h4>
<strip-select id="orders_scope_selector">
<strip-option value="active" selected>Open</strip-option>
<strip-option value="completed">Completed</strip-option>
</strip-select>
</div>
<div class="table" style="--table-columns: 4;">
<div class="table__header table__row">
<div></div>
<div>Quantity</div>
<div>At price</div>
<div>Order placed</div>
<div id="orders_section__header" class="flex">
<div id="orders_section__header--primary" class="flex w-100 align-center space-between">
<sm-tab-header target="orders_tab">
<sm-tab>My orders</sm-tab>
<sm-tab>Market orders</sm-tab>
</sm-tab-header>
<sm-select id="orders_scope_selector" class="tab" align-select="right">
<sm-option value="active" selected>Open</sm-option>
<sm-option value="completed">Completed</sm-option>
</sm-select>
</div>
<div id="orders_section__header--secondary"
class="flex w-100 align-center space-between hide-completely">
<button class="" onclick="clearSelection()" title="Clear all selection">
<svg xmlns="http://www.w3.org/2000/svg" class="icon button__icon--left" height="24px"
viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
</svg>
<span id="selected_orders"></span>
</button>
<div class="options">
<button class="button" title="Cancel selected orders" onclick="cancelAll()">
Cancel selected
</button>
</div>
</div>
</div>
<sm-tab-panels id="orders_tab">
<div class="table">
<ul id="my_order_list"></ul>
</div>
<div class="table">
<ul id="market_order_list">
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
sdgkmsdg <br>
</ul>
</div>
</sm-tab-panels>
</section>
<section id="user_section" class="grid">
<h4 class="flex align-center user_section__header">
@ -215,7 +250,7 @@
My wallet
</h4>
<div id="wallet_actions">
<p>Select asset</p>
<p class="label">Select asset</p>
<sm-select id="wallet_asset_selector">
<sm-option value="FLO">FLO</sm-option>
<sm-option value="Rupee">Rupee</sm-option>
@ -415,6 +450,28 @@
<span class="available-balance"></span>
</div>
</template>
<template id="order_template">
<li class="table__row order-card">
<sm-checkbox></sm-checkbox>
<div class="grid">
<div class="order-card__type capitalize"></div>
<div class="order-card__quantity"></div>
</div>
<div class="grid">
<span class="label order-card__price-type">Unit price</span>
<div class="order-card__price"></div>
</div>
<time class="order-card__time"></time>
<button class="cancel-order" title="Cancel this order">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z" />
</svg>
<span>Cancel</span>
</button>
</li>
</template>
<script id="ui_utils">
// Global variables
const domRefs = {};
@ -654,7 +711,7 @@
return `${month} ${year}`;
}
else
return `${month} ${date} ${year}, ${finalHours}`;
return `${finalHours}, ${month} ${date} ${year}`;
} catch (e) {
console.error(e);
return time;
@ -962,6 +1019,51 @@
getRef(containerId).append(card)
}
const myBuyOrders = [
{
id: 'dfs5g16sdg1',
time_placed: generateRandomDate(),
quantity: 14.5,
maxPrice: 1.36,
},
{
id: 'f4gd1d56fg1',
time_placed: generateRandomDate(),
quantity: 78.3,
maxPrice: 1.26,
},
{
id: 's4dg5s4d1g98',
time_placed: generateRandomDate(),
quantity: 14.5,
maxPrice: 1.28,
},
]
const mySellOrders = [
{
id: 'sd8g45g419s6',
time_placed: generateRandomDate(),
quantity: 15,
minPrice: 1.16,
},
{
id: 's59d1g9ws18d',
time_placed: generateRandomDate(),
quantity: 8.3,
minPrice: 1.86,
},
{
id: 'w899e1g4d1g98',
time_placed: generateRandomDate(),
quantity: 18.5,
minPrice: 1.64,
},
]
function generateRandomDate() {
return new Date() - Math.floor(Math.random() * 10000000000);
}
function account() {
getAccount().then(acc => {
console.debug(acc);
@ -983,28 +1085,9 @@
let rupee_net = rupee_total - rupee_locked;
console.debug("RUPEE", rupee_total, rupee_locked, rupee_net);
showBalance("rupee_balance", rupee_net, rupee_locked)
//My buy orders
let container = document.getElementById("my-buy-orders").getElementsByTagName("tbody")[0];
container.innerHTML = '';
acc.buyOrders.forEach(o => {
let row = container.insertRow();
row.insertCell().innerHTML = `<input type="checkbox">`;
row.insertCell().textContent = o.quantity;
row.insertCell().textContent = o.maxPrice;
row.insertCell().textContent = new Date(o.time_placed);
row.dataset["id"] = o.id;
});
//My sell orders
container = document.getElementById("my-sell-orders").getElementsByTagName("tbody")[0];
container.innerHTML = '';
acc.sellOrders.forEach(o => {
let row = container.insertRow();
row.insertCell().innerHTML = `<input type="checkbox">`;
row.insertCell().textContent = o.quantity;
row.insertCell().textContent = o.minPrice;
row.insertCell().textContent = new Date(o.time_placed);
row.dataset["id"] = o.id;
});
//My orders
renderOpenOrders(acc.buyOrders, acc.sellOrders)
//My Transactions
container = document.getElementById("my-transactions").getElementsByTagName("tbody")[0];
container.innerHTML = '';
@ -1098,6 +1181,21 @@
refresh(true);
</script>
<script>
const render = {
orderCard(orderDetails = {}) {
const { id, quantity, price, time, type } = orderDetails
const card = getRef('order_template').content.cloneNode(true).firstElementChild
card.dataset.id = id
card.dataset.type = type
card.querySelector('.order-card__type').textContent = type
card.querySelector('.order-card__quantity').textContent = `${quantity} FLO`
card.querySelector('.order-card__price-type').textContent = type === 'buy' ? 'Max price' : 'Min price'
card.querySelector('.order-card__price').textContent = `₹${price}`
card.querySelector('.order-card__time').textContent = getFormattedTime(time, true)
return card
}
}
function showProcess(id) {
getRef(id).children[0].classList.add('clip')
getRef(id).append(document.createElement('sm-spinner'))
@ -1202,6 +1300,160 @@
hideProcess('wallet_popup__cta_wrapper')
}
})
const selectedOrders = new Map()
const slideInLeft = [
{
opacity: 0,
transform: 'translateX(1.5rem)'
},
{
opacity: 1,
transform: 'translateX(0)'
}
]
const slideOutLeft = [
{
opacity: 1,
transform: 'translateX(0)'
},
{
opacity: 0,
transform: 'translateX(-1.5rem)'
},
]
const slideInRight = [
{
opacity: 0,
transform: 'translateX(-1.5rem)'
},
{
opacity: 1,
transform: 'translateX(0)'
}
]
const slideOutRight = [
{
opacity: 1,
transform: 'translateX(0)'
},
{
opacity: 0,
transform: 'translateX(1.5rem)'
},
]
getRef('my_order_list').addEventListener('change', e => {
const animOptions = {
duration: 150,
easing: 'ease',
fill: 'forwards'
}
const target = e.target.parentNode;
target.classList.toggle('order-card--selected')
if (e.target.checked) {
selectedOrders.set(target.dataset.id, target.dataset.type)
} else {
selectedOrders.delete(target.dataset.id)
}
getRef('selected_orders').textContent = `${selectedOrders.size} selected`
if (selectedOrders.size === 1 && !getRef('orders_section__header').children[0].classList.contains('hide-completely')) {
getRef('orders_section__header').children[0].animate(slideOutLeft, animOptions)
.onfinish = () => {
getRef('orders_section__header').children[0].classList.add('hide-completely')
getRef('orders_section__header').children[1].classList.remove('hide-completely')
getRef('orders_section__header').children[1].animate(slideInLeft, animOptions)
}
} else if (selectedOrders.size === 0 && getRef('orders_section__header').children[0].classList.contains('hide-completely')) {
getRef('orders_section__header').children[1].animate(slideOutRight, animOptions)
.onfinish = () => {
getRef('orders_section__header').children[1].classList.add('hide-completely')
getRef('orders_section__header').children[0].classList.remove('hide-completely')
getRef('orders_section__header').children[0].animate(slideInRight, animOptions)
}
}
})
function clearSelection() {
getRef('my_order_list').querySelectorAll('sm-checkbox[checked]').forEach(elem => elem.checked = false)
}
getRef('my_order_list').addEventListener('click', e => {
if (e.target.closest('.cancel-order')) {
getConfirmation('Cancel this order?').then(res => {
if (res) {
const target = e.target.closest('.order-card')
const id = target.dataset.id
const type = target.dataset.type
cancelOrder(type, id, proxy.secret)
.then(() => {
notify('Order cancelled', 'success')
target.remove()
})
.catch(err => notify(err, 'error'))
}
})
}
})
function cancelAll() {
getConfirmation('Cancel all selected orders?').then(res => {
if (res) {
try {
selectedOrders.forEach(async (type, id) => {
await cancelOrder(type, id, proxy.secret)
.then(() => {
getRef('my_order_list').querySelector(`data-id="${id}"`).remove()
})
})
notify('All selected orders cancelled', 'success')
}
catch (err) {
notify(err, 'error')
}
}
})
}
function renderAllOrders(buyOrders, sellOrders) {
getRef('my_order_list').innerHTML = '';
const frag = document.createDocumentFragment()
const allOpenOrders = [...(buyOrders || myBuyOrders), ...(sellOrders || mySellOrders)].sort((a, b) => b.time_placed - a.time_placed)
console.log(allOpenOrders)
allOpenOrders.forEach(order => {
const { id, quantity, minPrice = undefined, maxPrice = undefined, time_placed } = order
const orderDetails = {
id,
quantity,
type: minPrice ? 'sell' : 'buy',
price: minPrice || maxPrice,
time: time_placed
}
frag.append(render.orderCard(orderDetails))
})
getRef('my_order_list').append(frag)
}
function renderOpenOrders(buyOrders, sellOrders) {
getRef('my_order_list').innerHTML = '';
const frag = document.createDocumentFragment()
const allOpenOrders = [...(buyOrders || myBuyOrders), ...(sellOrders || mySellOrders)].sort((a, b) => b.time_placed - a.time_placed)
allOpenOrders.forEach(order => {
const { id, quantity, minPrice = undefined, maxPrice = undefined, time_placed } = order
const orderDetails = {
id,
quantity,
type: minPrice ? 'sell' : 'buy',
price: minPrice || maxPrice,
time: time_placed
}
frag.append(render.orderCard(orderDetails))
})
getRef('my_order_list').append(frag)
}
renderOpenOrders()
getRef('orders_scope_selector').addEventListener('change', e => {
if (e.target.value === 'open') {
renderOpenOrders()
} else {
renderCompletedOrders()
}
})
</script>
</body>