Skip to content

Commit

Permalink
HF 29 operations, NFT transfer URL
Browse files Browse the repository at this point in the history
  • Loading branch information
1aerostorm committed Oct 28, 2023
1 parent 9877e3f commit 1615833
Show file tree
Hide file tree
Showing 22 changed files with 1,099 additions and 26 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"formik": "^2.2.9",
"git-rev-sync": "^3.0.1",
"gmail-send": "^1.8.10",
"golos-lib-js": "^0.9.54",
"golos-lib-js": "^0.9.60",
"iron-session": "6.0.4",
"jspdf": "^2.3.0",
"lodash": "^4.17.11",
Expand Down
7 changes: 6 additions & 1 deletion src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
@include foundation-everything(true);

@import "./elements/AccountMenu";
@import "./elements/DropdownMenu";
@import "./elements/GeneratedPasswordInput";
@import "./elements/Expandable";
@import "./elements/LoadingIndicator";
@import "./elements/nft/NFTSmallIcon";
@import "./elements/nft/NFTTokens";
@import "./elements/PagedDropdownMenu";
@import "./elements/VerticalMenu";

@import "./modules/Header";
@import "./modules/LoginForm";
Expand All @@ -21,7 +26,7 @@
@import "./pages/login";
@import "./pages/register";
@import "./pages/sign/transfer";
@import "./pages/sign/transfer";
@import "./pages/sign/transfer_nft";
@import "./pages/oauth/[client]/[perms]";

@import "./foundation-overrides";
Expand Down
73 changes: 73 additions & 0 deletions src/elements/DropdownMenu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react'

import VerticalMenu from '@/elements/VerticalMenu'
import { findParent } from '@/utils/DomUtils'

export default class DropdownMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
shown: false,
selected: props.selected
};
}

componentWillUnmount() {
document.removeEventListener('click', this.hide);
}

toggle = (e) => {
const {shown} = this.state
if(shown) this.hide(e)
else this.show(e)
}

show = (e) => {
e.preventDefault();
this.setState({shown: true});
setTimeout(() => {
document.addEventListener('click', this.hide)
}, 1)
};

hide = (e) => {
// Do not hide the dropdown if there was a click within it.
const inside_dropdown = !!findParent(e.target, 'VerticalMenu');
if (inside_dropdown) return;

e.preventDefault();
this.setState({shown: false});
document.removeEventListener('click', this.hide);
};

navigate = (e) => {
const a = e.target.nodeName.toLowerCase() === 'a' ? e.target : e.target.parentNode;
this.setState({show: false});
if (a.host !== window.location.host) return;
e.preventDefault();
window.location.href = a.pathname + a.search
};

getSelectedLabel = (items, selected) => {
const selectedEntry = items.find(i => i.value === selected)
const selectedLabel = selectedEntry && selectedEntry.label ? selectedEntry.label : selected
return selectedLabel
}

render() {
const {el, items, selected, children, className, title, href, onClick, noArrow, hideSelected} = this.props;
const hasDropdown = items.length > 0

let entry = children || <span key='label'>
{this.getSelectedLabel(items, selected)}
{hasDropdown && !noArrow && <img src='/icons/dropdown-arrow.svg' alt='' width='20' height='20' style={{ paddingRight: '3px', marginTop: '5px', marginLeft: '2px' }}/>}
</span>

if(hasDropdown) entry = <a key="entry" href={href || '#'} onClick={onClick ? (e) => { onClick(e); this.toggle(e) } : this.toggle}>{entry}</a>

const menu = <VerticalMenu key="menu" title={title} items={items} hideValue={hideSelected ? selected : null} className="VerticalMenu" />;
const cls = 'DropdownMenu' + (this.state.shown ? ' show' : '') + (className ? ` ${className}` : '')
return React.createElement(el, {className: cls}, [entry, menu]);
}
}

53 changes: 53 additions & 0 deletions src/elements/DropdownMenu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.DropdownMenu {
position: relative;
display: inline-block;

.Icon.dropdown-arrow {
top: 2px;
margin-right: 0;
}

> .VerticalMenu {
visibility: hidden;
// min-width: 145px;
min-width: 232px;
z-index: 1000;

background-color: white;

display: block;
border: 1px solid $medium-gray;
border-radius: $global-radius;
opacity: 0;
position: absolute;
top: 100%;

// width: auto;
transform: translateY(10%);
transition: all 0.3s ease 0s, visibility 0s linear 0.3s;
box-shadow: 1px 1px 5px 0px rgba(50, 50, 50, 0.75);
}

&.show > .VerticalMenu {
visibility: visible;
opacity: 1;
transform: translateX(0%);
transition-delay: 0s;
}

.DropdownMenu.move-left {
.VerticalMenu {
left: -50%;
}
}

&.above > .VerticalMenu {
bottom: 100%;
top: auto;
}

&.top-most > .VerticalMenu {
position: fixed;
top: auto;
}
}
152 changes: 152 additions & 0 deletions src/elements/PagedDropdownMenu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import React from 'react'
import cloneDeep from 'lodash/cloneDeep'
import isEqual from 'lodash/isEqual'
import tt from 'counterpart'

import DropdownMenu from '@/elements/DropdownMenu'
import LoadingIndicator from '@/elements/LoadingIndicator'

const hideLastItem = true;

export default class PagedDropdownMenu extends React.Component {
static defaultProps = {
page: 1
}

constructor(props) {
super(props);
this.state = {
items: [],
page: props.page,
loading: false,
};
}

componentDidMount() {
const { items, page, } = this.props
this.initItems(this.sliceItems(items, page))
this.setState({ page })
}

componentDidUpdate(prevProps) {
const { items, page, } = this.props
if (items && (!prevProps.items || !isEqual(items, prevProps.items))) {
const sliced = this.sliceItems(items, 1)
this.initItems(sliced)
this.setState({ page: 1 })
} else if (page && prevProps.page !== page) {
this.setState({ page })
}
}

sliceItems = (items, page) => {
const { onLoadMore, perPage } = this.props
if (onLoadMore) {
return items
}
const startIdx = perPage * (page - 1)
const endIdx = startIdx + perPage + 1
const sliced = items.slice(startIdx, endIdx)
return sliced
}

initItems = (items) => {
if (!items || !items.length)
return;
this.setState({
items: cloneDeep(items),
});
};

loadMore = async (newPage) => {
const { items, page, } = this.state;
const { onLoadMore, } = this.props;
if (!onLoadMore) {
setTimeout(async () => {
this.setState({
page: newPage
}, () => {
this.initItems(this.sliceItems(this.props.items, newPage))
})
}, 10);
return
}
setTimeout(async () => {
this.setState({
page: newPage,
loading: true,
});
if (onLoadMore) {
const res = await onLoadMore({ page, newPage, items, });
this.setState({
loading: false,
});
this.initItems(res);
}
}, 10);
};

nextPage = () => {
const { page, } = this.state;
this.loadMore(page + 1);
};

prevPage = () => {
if (this.state.page === 1) return;
const { page, } = this.state;
this.loadMore(page - 1);
};

_renderPaginator = () => {
const { perPage, } = this.props;
const { items, page, } = this.state;
const hasMore = items.length > perPage;
if (page === 1 && !hasMore) {
return null;
}
const hasPrev = page > 1
return {
value: <span>
<span className={'PagedDropdownMenu__paginator' + (hasPrev ? '' : ' disabled')} onClick={this.prevPage}>
{hasPrev ? '< ' + tt('g.back') : ''}</span>
<span className={'PagedDropdownMenu__paginator' + (hasMore ? '' : ' disabled')} onClick={hasMore ? this.nextPage : null}>
{hasMore ? tt('g.more_list') + ' >' : ''}
</span></span>,
};
};

render() {
const { el, selected, children, className, title, href, noArrow, perPage, renderItem, hideSelected, } = this.props
const { items, loading, } = this.state;

let itemsWithPaginator = [];
if (!loading) {
for (let i = 0; i < items.length; ++i) {
const rendered = renderItem(items[i])
itemsWithPaginator.push(rendered)
}
if (items.length > perPage && hideLastItem) {
itemsWithPaginator.pop();
}
const paginator = this._renderPaginator();
if (paginator) {
itemsWithPaginator.push(paginator);
}
} else {
itemsWithPaginator = [{value: <span>
<LoadingIndicator type='circle' />
</span>}];
}

return (<DropdownMenu
children={children}
title={title}
href={href}
noArrow={noArrow}
className={className}
items={itemsWithPaginator}
selected={selected}
hideSelected={hideSelected}
el={el} />)
}
};
16 changes: 16 additions & 0 deletions src/elements/PagedDropdownMenu.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.PagedDropdownMenu__paginator {
display: inline-block !important;
text-align: center;
color: #0078C4 !important;
font-weight: normal !important;
font-size: 100% !important;
user-select: none;
cursor: pointer;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
width: 50%;

&.disabled {
cursor: auto;
}
}
36 changes: 36 additions & 0 deletions src/elements/VerticalMenu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';

import LinkEx from '@/utils/LinkEx'

export default class VerticalMenu extends React.Component {
closeMenu = (e) => {
// If this was not a left click, or if CTRL or CMD were held, do not close the menu.
if(e.button !== 0 || e.ctrlKey || e.metaKey) return;

// Simulate clicking of document body which will close any open menus
document.body.click();
}

render() {
const {items, title, description, className, hideValue} = this.props;
return <ul className={'VerticalMenu menu vertical' + (className ? ' ' + className : '')}>
{title && <li className="title">{title}</li>}
{description && <li className="description">{description}</li>}
{items.map((i, k) => {
if(i.value === hideValue) return null
const target = i.target
return <li data-link={i.link} data-value={i.value} key={i.key ? i.key : i.value} onClick={i.link ? this.closeMenu : null} style={i.style}>
{i.link ? <LinkEx href={i.link} target={target} onClick={i.onClick}>
{i.icon}{i.label ? i.label : i.value}
{i.data && <span>{i.data}</span>}
&nbsp; {i.addon}
</LinkEx> :
<span>
{i.icon}{i.label ? i.label : i.value}
</span>
}
</li>
})}
</ul>;
}
}
Loading

0 comments on commit 1615833

Please sign in to comment.