Feature update and bug fixes

-- added UI for multisig creation
This commit is contained in:
sairaj mote 2022-08-24 17:05:55 +05:30
parent 19a83a9035
commit 71b2f2557c
5 changed files with 2976 additions and 2459 deletions

View File

@ -39,7 +39,7 @@ body {
--orange: #ff9100;
--redish-orange: #ff3d00;
color: rgba(var(--text-color), 1);
background: rgba(var(--foreground-color), 1);
background: rgba(var(--background-color), 1);
overflow-y: hidden;
}
body #scroll_to_bottom {
@ -91,14 +91,19 @@ strong {
color: rgba(var(--text-color), 0.9);
}
.warning {
.info {
line-height: normal;
padding: 1rem;
background-color: khaki;
border-radius: 0.5rem;
font-weight: 500;
color: rgba(0, 0, 0, 0.7);
}
.info--warning {
background-color: khaki;
}
.info--error {
background-color: var(--danger-color);
}
a {
text-decoration: none;
@ -120,7 +125,6 @@ button,
.button {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: relative;
display: inline-flex;
@ -164,6 +168,10 @@ button:not(:disabled),
.button--small {
padding: 0.4rem 0.6rem;
}
.button--outlined {
border: solid rgba(var(--text-color), 0.5) 0.1rem;
background-color: rgba(var(--foreground-color), 1);
}
.cta {
text-transform: uppercase;
@ -207,7 +215,6 @@ details summary {
display: flex;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
align-items: center;
@ -241,7 +248,7 @@ sm-copy {
sm-input,
sm-textarea {
font-size: 0.9rem;
--border-radius: 0.3rem;
--border-radius: 0.5rem;
--background-color: rgba(var(--foreground-color), 1);
}
sm-input button .icon,
@ -298,7 +305,6 @@ strip-option {
--border-radius: 0.2rem;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@ -359,7 +365,6 @@ ol li::before {
word-wrap: break-word;
word-break: break-word;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
}
@ -972,10 +977,10 @@ sm-button[variant=primary] {
.initial {
position: relative;
justify-content: center;
font-size: 1.2rem;
font-weight: 500;
width: 2.8rem;
height: 2.8rem;
font-size: 1.1rem;
font-weight: 700;
width: 2.4rem;
height: 2.4rem;
aspect-ratio: 1/1;
color: rgba(var(--foreground-color), 1);
box-shadow: 0 0.1rem 0.1rem rgba(0, 0, 0, 0.06);
@ -983,9 +988,21 @@ sm-button[variant=primary] {
text-transform: uppercase;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
overflow: hidden;
background-color: var(--contact-color, --accent-color);
flex-shrink: 0;
}
.initial::after {
content: "";
position: absolute;
background-color: rgba(255, 255, 255, 0.2);
width: 100%;
height: 200%;
margin-left: 50%;
margin-top: 50%;
transform: rotate(45deg);
transform-origin: left center;
}
.group-icon {
@ -998,12 +1015,10 @@ sm-button[variant=primary] {
position: relative;
display: grid;
gap: 0 1rem;
padding: 0.8rem 1.5rem;
align-items: center;
flex-shrink: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
overflow: hidden;
}
@ -1035,6 +1050,7 @@ sm-button[variant=primary] {
width: 100%;
font-size: 1em;
font-weight: 500;
margin-bottom: 0.3rem;
}
.contact .span-2 {
display: flex;
@ -1046,6 +1062,7 @@ sm-button[variant=primary] {
.contact .last-message {
font-weight: 400;
font-size: 0.9em;
opacity: 0.9;
}
.contact .menu {
justify-self: flex-end;
@ -1067,11 +1084,16 @@ sm-button[variant=primary] {
}
.selectable-contact {
margin: 0 -0.3rem;
padding: 0.5rem 0.3rem;
border-radius: 0.5rem;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.selectable-contact:not(:last-of-type) {
margin-bottom: 0.2rem;
}
.selectable-contact input {
margin-left: auto;
}
@ -1081,6 +1103,7 @@ sm-button[variant=primary] {
align-items: center;
}
.group-member .admin-tag {
white-space: nowrap;
margin-left: auto;
padding: 0.1rem 0.6rem;
font-size: 0.8rem;
@ -1194,18 +1217,24 @@ sm-button[variant=primary] {
}
#warn_no_encryption {
background: #fffd8d;
background: rgb(255, 253, 141);
color: #111;
}
.contact .initial::after,
.mail-card .initial::after {
.contact,
.mail-card {
padding: 0.8rem;
margin: 0 0.2rem;
border-radius: 0.5rem;
}
.contact::before,
.mail-card::before {
content: "";
position: absolute;
bottom: -0.1rem;
right: -0.1rem;
height: 1rem;
width: 1rem;
top: 0;
margin: 0.5rem;
padding: 0.3rem;
background: var(--accent-color);
border-radius: 100%;
border: solid rgba(var(--foreground-color), 1) 1px;
@ -1213,8 +1242,8 @@ sm-button[variant=primary] {
transition: transform 0.3s;
}
.contact.unread .initial::after,
.mail-card.unread .initial::after {
.contact.unread::before,
.mail-card.unread::before {
transform: scale(1);
}
@ -1241,12 +1270,10 @@ sm-button[variant=primary] {
position: relative;
display: grid;
gap: 0 1rem;
padding: 0.8rem 1.5rem;
align-items: center;
flex-shrink: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
grid-template-columns: auto 1fr auto;
grid-template-areas: "dp sender date" "dp subject subject" "dp desc desc";
@ -1254,8 +1281,6 @@ sm-button[variant=primary] {
.mail-card .initial {
grid-area: dp;
align-self: flex-start;
height: 2rem;
width: 2rem;
font-size: 1rem;
}
.mail-card .sender {
@ -1380,7 +1405,7 @@ sm-button[variant=primary] {
#main_navbar {
display: flex;
background: rgba(var(--text-color), 0.03);
background: rgba(var(--foreground-color), 1);
}
#main_navbar.hide-away {
position: absolute;
@ -1396,23 +1421,26 @@ sm-button[variant=primary] {
.nav-item {
position: relative;
display: flex;
flex: 1;
display: grid;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.5rem 0.3rem;
justify-items: center;
padding: 0.5rem 0.4rem;
color: var(--text-color);
font-size: 0.8rem;
border-radius: 0.3rem;
border-radius: 0.5rem;
font-weight: 500;
aspect-ratio: 1/1;
}
.nav-item .icon {
grid-area: 1/1/2/2;
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.nav-item .filled {
opacity: 0;
}
.nav-item__title {
margin-top: 0.3rem;
line-height: 1;
transition: opacity 0.2s, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.nav-item--active {
@ -1422,6 +1450,12 @@ sm-button[variant=primary] {
fill: var(--accent-color);
transform: translateY(50%);
}
.nav-item--active .icon.filled {
opacity: 1;
}
.nav-item--active .icon:not(.filled) {
opacity: 0;
}
.nav-item--active .nav-item__title {
transform: translateY(100%);
opacity: 0;
@ -1458,6 +1492,37 @@ sm-button[variant=primary] {
margin: 0.3rem;
}
#contacts,
#mails,
#settings {
height: 100%;
overflow-y: hidden;
}
#contacts .header,
#mails .header,
#settings .header {
padding: 1rem;
position: relative;
gap: 0.5rem;
min-height: 4rem;
}
#contacts .header sm-input,
#mails .header sm-input,
#settings .header sm-input {
width: 100%;
}
#contacts .header h4,
#mails .header h4,
#settings .header h4 {
text-transform: capitalize;
font-weight: 500;
}
#contacts .header sm-menu,
#mails .header sm-menu,
#settings .header sm-menu {
margin-right: -0.7rem;
}
#auto_complete_contact {
position: relative;
}
@ -1484,6 +1549,11 @@ sm-button[variant=primary] {
#contacts {
position: relative;
overflow-x: hidden;
grid-template-rows: -webkit-max-content -webkit-max-content 1fr;
grid-template-rows: max-content max-content 1fr;
}
#contacts .header {
padding: 0.5rem 1rem;
}
#contacts .scrolling-wrapper {
height: 100%;
@ -1499,7 +1569,7 @@ sm-button[variant=primary] {
padding: 0.5rem 0;
}
#group_creation_process .group-icon {
#creation_process .group-icon {
background-color: var(--accent-color);
justify-self: center;
height: 8rem;
@ -1510,7 +1580,6 @@ sm-button[variant=primary] {
font-size: 4rem;
}
#contacts,
#mails {
position: relative;
grid-template-rows: -webkit-max-content 1fr;
@ -1533,72 +1602,8 @@ sm-button[variant=primary] {
margin: 0rem;
}
#contacts,
#mails,
#settings {
height: 100%;
overflow-y: hidden;
}
#contacts .header,
#mails .header,
#settings .header {
padding: 1rem 1.5rem;
position: relative;
gap: 0.5rem;
min-height: 4rem;
}
#contacts .header .expanding-search,
#mails .header .expanding-search,
#settings .header .expanding-search {
position: absolute;
width: 100%;
padding: 0.7rem 1.5rem;
background: rgba(var(--foreground-color), 1);
}
#contacts .header sm-input,
#mails .header sm-input,
#settings .header sm-input {
width: 100%;
}
#contacts .header h4,
#mails .header h4,
#settings .header h4 {
text-transform: capitalize;
font-weight: 500;
}
#contacts .header .flex h4,
#mails .header .flex h4,
#settings .header .flex h4 {
flex: 1;
}
#contacts .header .flex sm-menu,
#mails .header .flex sm-menu,
#settings .header .flex sm-menu {
margin-right: -0.7rem;
}
#contacts .header sm-button,
#mails .header sm-button,
#settings .header sm-button {
margin: 0;
}
#contacts .header sm-button .icon,
#mails .header sm-button .icon,
#settings .header sm-button .icon {
height: 0.9rem;
width: 0.9rem;
align-self: center;
stroke-width: 8;
margin-left: 0;
margin-right: 0.5rem;
}
#chat_search_field .icon-only {
margin-left: -0.5rem;
margin-right: 0.5rem;
}
#search_chats {
--min-height: 3rem;
--min-height: 2.5rem;
}
#chat_page,
@ -1642,7 +1647,7 @@ sm-button[variant=primary] {
max-width: max-content;
margin-bottom: 0.2rem;
margin-top: 0.8rem;
padding: 0.6em 1em;
padding: 0.5em 0.8em;
transition: opacity 0.3s, transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.message .sender-name {
@ -1659,8 +1664,7 @@ sm-button[variant=primary] {
word-break: break-all;
word-break: break-word;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
hyphens: auto;
white-space: pre-wrap;
line-height: 1.5;
}
@ -1686,7 +1690,7 @@ sm-button[variant=primary] {
.sent {
margin-left: auto;
background: var(--accent-color);
border-radius: 0.8rem 0 0.8rem 0.8rem;
border-radius: 0.3rem 0 0.3rem 0.3rem;
}
.sent > * {
color: rgba(var(--background-color), 1);
@ -1705,8 +1709,8 @@ sm-button[variant=primary] {
.received {
margin-right: auto;
background: rgba(var(--text-color), 0.1);
border-radius: 0 0.8rem 0.8rem 0.8rem;
border-radius: 0 0.3rem 0.3rem 0.3rem;
background-color: rgba(var(--text-color), 0.1);
}
.received::after {
content: "";
@ -1732,12 +1736,12 @@ sm-button[variant=primary] {
.sent + .sent,
.received + .received {
border-radius: 0.8rem;
border-radius: 0.3rem;
}
.distinct-sender {
display: grid;
border-radius: 0 0.8rem 0.8rem 0.8rem !important;
border-radius: 0 0.3rem 0.3rem 0.3rem !important;
margin-top: 0.8rem !important;
}
.distinct-sender::after {
@ -1879,7 +1883,6 @@ sm-button[variant=primary] {
#type_message {
margin: 0;
--border-radius: 0.5rem;
--background: rgba(var(--text-color), 0.1);
}
@ -1900,7 +1903,6 @@ sm-button[variant=primary] {
--button-hover-background: rgba(var(--text-color), 0.2);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
width: 100%;
max-height: 40vh;
@ -1913,7 +1915,6 @@ sm-button[variant=primary] {
border-radius: 0.6rem;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: center;
}
@ -1937,6 +1938,7 @@ sm-button[variant=primary] {
}
#chats_list {
gap: 0.2rem;
padding-bottom: 6rem;
}
@ -2106,7 +2108,6 @@ sm-button[variant=primary] {
sm-popup {
--border-radius: 1rem 1rem 0 0;
}
#landing {
grid-template-areas: "illustration" ".";
align-items: flex-start;
@ -2120,11 +2121,9 @@ sm-button[variant=primary] {
display: flex;
width: 100% !important;
}
#landing_illustration {
grid-area: illustration;
}
.frame form,
#sign_in form {
height: 100%;
@ -2137,23 +2136,50 @@ sm-button[variant=primary] {
#sign_in sm-button[variant=primary] {
margin-top: auto;
}
.fab {
bottom: 4rem;
}
.inner-page {
margin-bottom: 5rem;
}
#main_navbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: -webkit-max-content;
width: -moz-max-content;
width: max-content;
margin: 0.5rem auto;
border-radius: 1rem;
justify-content: center;
background-color: transparent;
-webkit-backdrop-filter: blur(0.5rem);
backdrop-filter: blur(0.5rem);
background-color: rgba(var(--foreground-color), 0.9);
}
#main_navbar ul {
background-color: rgba(var(--text-color), 0.05);
border-radius: inherit;
box-shadow: 0 1rem 1.5rem rgba(0, 0, 0, 0.16);
}
#main_navbar.hide-away {
bottom: 0;
left: 0;
right: 0;
}
#main_navbar .nav-item {
height: 3.8rem;
width: 4.5rem;
}
#chats_list,
#contact_container {
gap: 0.2rem;
}
#chat_view .message {
width: auto;
max-width: 90%;
}
#chat_header {
grid-template-columns: auto minmax(0, 1fr);
}
@ -2161,7 +2187,6 @@ sm-button[variant=primary] {
max-width: calc(100% - 2rem);
margin-left: -1.7rem;
}
#settings {
overflow-x: hidden;
}
@ -2176,7 +2201,6 @@ sm-button[variant=primary] {
-webkit-backdrop-filter: blur(0.5rem);
backdrop-filter: blur(0.5rem);
}
.hide-on-mobile {
display: none !important;
}
@ -2185,38 +2209,31 @@ sm-button[variant=primary] {
.hide-on-desktop {
display: none !important;
}
.page {
padding-bottom: 0;
}
.popup__header {
grid-column: 1/-1;
padding: 1rem 1.5rem 0 1.5rem;
}
.logo-section {
padding: 2rem 3rem 0 3rem;
margin: 0.5rem 0;
}
sm-popup {
--width: 24rem;
--min-width: 24rem;
--border-radius: 0.5rem;
}
#landing {
align-items: center;
gap: 4vw;
grid-template-columns: 1fr 1fr;
padding: 0 4vw;
}
#sign_in {
width: 24rem;
}
#main_page {
grid-template-columns: -webkit-min-content 1fr;
grid-template-columns: min-content 1fr;
@ -2225,26 +2242,20 @@ sm-button[variant=primary] {
overflow: hidden;
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05), 0 1rem 3rem rgba(0, 0, 0, 0.2);
}
#main_navbar {
grid-area: nav;
border-top: none;
flex-direction: column;
background-color: rgba(var(--foreground-color), 0.3);
border-right: solid thin rgba(var(--text-color), 0.1);
background-color: rgba(var(--foreground-color), 0.3);
}
#main_navbar ul {
flex-direction: column;
gap: 0.5rem;
padding: 0.3rem;
}
#main_navbar ul li:last-of-type {
margin-top: auto;
}
.nav-item {
aspect-ratio: 1/1;
}
.nav-item__indicator {
width: 0.25rem;
height: 50%;
@ -2252,26 +2263,21 @@ sm-button[variant=primary] {
border-radius: 0 1rem 1rem 0;
bottom: auto;
}
#add_contact_popup {
--min-width: 24rem;
}
#compose_mail_popup,
#reply_mail_popup {
--min-width: 36rem;
}
#emoji_picker {
max-height: 18rem;
}
#chat_view .message {
width: auto;
align-self: flex-start;
max-width: 55ch;
}
#chat_page,
#mail_page {
display: grid;
@ -2281,7 +2287,6 @@ sm-button[variant=primary] {
#mail_page > :first-child {
border-right: solid thin rgba(var(--text-color), 0.1);
}
#settings {
display: grid;
grid-template-columns: 14rem 1fr;
@ -2300,12 +2305,10 @@ sm-button[variant=primary] {
#settings .panel > *:first-of-type {
margin-top: 0.5rem;
}
.contact.active,
.mail-card.active {
background: rgba(var(--text-color), 0.06);
}
#contact_details_popup.is-group {
--width: 52rem;
}
@ -2329,7 +2332,6 @@ sm-button[variant=primary] {
padding: 1rem 4rem;
padding-top: 0;
}
#chat_page,
#mail_page {
grid-template-columns: 21rem 1fr;
@ -2343,11 +2345,9 @@ sm-button[variant=primary] {
#landing .title-font {
font-size: 3rem;
}
#emoji_picker {
--num-columns: 16;
}
.contact.active,
.mail-card.active {
background: rgba(var(--text-color), 0.06);
@ -2358,7 +2358,6 @@ sm-button[variant=primary] {
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb {
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;
@ -2366,21 +2365,17 @@ sm-button[variant=primary] {
::-webkit-scrollbar-thumb:hover {
background: rgba(var(--text-color), 0.5);
}
.interactive:hover {
background-color: rgba(var(--text-color), 0.06);
}
.emoji:hover {
cursor: pointer;
background: rgba(var(--text-color), 0.06);
}
.contact .menu {
opacity: 0;
transition: opacity 0.3s;
}
.contact:hover .menu {
opacity: 1;
}
@ -2390,7 +2385,6 @@ sm-button[variant=primary] {
.contact {
-webkit-tap-highlight-color: transparent;
}
.contact .menu {
display: none;
}
@ -2400,6 +2394,6 @@ sm-button[variant=primary] {
overflow: overlay;
}
}
.hide {
.hidden {
display: none !important;
}

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@
function onLoadStartUp() {
showPage('loading')
floDapps.setAppObjectStores({ userSettings: {} })
document.body.classList.remove('hide')
document.body.classList.remove('hidden')
floDapps.setCustomPrivKeyInput(getSignedIn)
getRef('emoji_picker').shadowRoot.append(style);
@ -61,7 +61,7 @@
<link rel="stylesheet" href="css/style.css">
</head>
<body data-theme="dark" onload="onLoadStartUp()" class="hide">
<body data-theme="dark" onload="onLoadStartUp()" class="hidden">
<sm-notifications id="notification_drawer"></sm-notifications>
<sm-popup id="confirmation_popup">
<h4 id="confirm_title"></h4>
@ -82,7 +82,7 @@
</div>
</sm-form>
</sm-popup>
<div id="secondary_pages" class="page hide">
<div id="secondary_pages" class="page hidden">
<header class="flex align-center gap-1">
<div class="flex align-center flex-1">
<svg class="icon flo-icon" width="64" height="64" viewBox="0 0 64 64" fill="none"
@ -102,7 +102,7 @@
</div>
<theme-toggle></theme-toggle>
</header>
<div id="landing" class="grid inner-page hide">
<div id="landing" class="grid inner-page hidden">
<div class="left">
<h4>
FLO Messenger
@ -122,7 +122,7 @@
<img src="assets/message-background.svg" alt="">
</div>
</div>
<article id="sign_in" class="inner-page page-layout hide">
<article id="sign_in" class="inner-page page-layout hidden">
<section>
<h1 style="font-size: 2rem;">Sign In</h1>
<p>Welcome back, glad to see you again</p>
@ -136,7 +136,7 @@
</p>
</section>
</article>
<article id="sign_up" class="inner-page page-layout hide">
<article id="sign_up" class="inner-page page-layout hidden">
<section class="grid gap-1-5">
<div id="flo_id_warning" class="grid justify-center gap-0-5">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
@ -163,7 +163,7 @@
too. </p>
</section>
</article>
<div id="loading" class="inner-page hide">
<div id="loading" class="inner-page hidden">
<svg class="page__loader" viewBox="0 0 512 512">
<defs>
<style>
@ -214,47 +214,31 @@
<button class="button" onclick="floDapps.clearCredentials()">Reset</button>
</div>
</div>
<main id="main_page" class="page grid hide">
<main id="main_page" class="page grid hidden">
<section id="chat_page" class="inner-page">
<div id="contacts" class="grid">
<header class="grid header">
<div class="flex align-center">
<h4>FLO Messenger</h4>
<button class="icon-only" onclick="toggleSearch('chat_search_field')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<sm-input id="search_chats" class="margin-right-0-5" type="search"
placeholder="Search FLO ID or name">
<svg slot="icon" class="icon" xmlns="http://www.w3.org/2000/svg" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
</svg>
</button>
</sm-input>
<sm-menu align-options="right">
<sm-menu-option onclick="openPopup('group_creation_popup')">
<sm-menu-option onclick="openCreationPopup('group')">
Create new group
</sm-menu-option>
</sm-menu>
</div>
<div id="chat_search_field" class="expanding-search flex align-center hide">
<button class="icon-only" onclick="toggleSearch('chat_search_field')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none"></path>
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path>
</svg>
</button>
<sm-input id="search_chats" type="search" placeholder="Search FLO ID or name"></sm-input>
</div>
</header>
<button id="new_message_button" onclick="openPopup('new_message_popup')"
class="button button--primary fab round">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM5.92 19H5v-.92l9.06-9.06.92.92L5.92 19zM20.71 5.63l-2.34-2.34c-.2-.2-.45-.29-.71-.29s-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41z" />
</svg>
New chat
</button>
<div class="margin-bottom-0-5" style="padding: 0 1rem;">
<button class="button button--small button--outlined" onclick="openCreationPopup('multisig')">Create
multisig</button>
</div>
<div id="chats_list" class="flex observe-empty-state"></div>
<div class="empty-state flex flex-direction-column align-center text-center align-self-center">
<svg class="icon icon--big" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
@ -266,8 +250,18 @@
<h4 class="margin-bottom-0-5">Start your first conversation</h4>
<p>Tap/click on 'New chat' to add or select a contact.</p>
</div>
<button id="new_message_button" onclick="openPopup('new_message_popup')"
class="button button--primary fab round">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM5.92 19H5v-.92l9.06-9.06.92.92L5.92 19zM20.71 5.63l-2.34-2.34c-.2-.2-.45-.29-.71-.29s-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41z" />
</svg>
New chat
</button>
</div>
<div id="chat_view" class="grid hide">
<div id="chat_view" class="grid hidden">
<header id="chat_header" class="grid align-center">
<a class="hide-on-desktop button icon-only back-button" href="#/chat_page">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
@ -297,7 +291,7 @@
</button>
</div>
<footer id="chat_footer" class="grid">
<emoji-picker id="emoji_picker" class="hide"></emoji-picker>
<emoji-picker id="emoji_picker" class="hidden"></emoji-picker>
<div class="flex">
<svg id="emoji_toggle" onclick="toggleEmoji('toggle')" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 64 64">
@ -310,7 +304,7 @@
</footer>
</div>
</section>
<section class="inner-page hide" id="mail_page">
<section class="inner-page hidden" id="mail_page">
<div id="mails" class="grid">
<header class="grid header">
<div class="flex align-center space-between">
@ -345,7 +339,7 @@
<p>Tap/click on 'New Mail' button below to compose new mail.</p>
</div>
</div>
<div class="hide flex h-100">
<div class="hidden flex h-100">
<ul id="sent_mail_container" class="mail-container flex observe-empty-state"></ul>
<div class="empty-state flex flex-direction-column align-center text-center align-self-center">
<svg class="icon icon--big" xmlns="http://www.w3.org/2000/svg"
@ -369,7 +363,7 @@
</div>
</div>
</div>
<div id="mail" class="flex hide-on-mobile hide">
<div id="mail" class="flex hide-on-mobile hidden">
<div id="mail_container"></div>
<div class="flex">
<sm-button id="prev_mail">View Previous Mail</sm-button>
@ -377,7 +371,7 @@
</div>
</div>
</section>
<section id="settings" class="inner-page hide">
<section id="settings" class="inner-page hidden">
<aside id="settings_sidebar">
<header class="grid header">
<h4>Settings</h4>
@ -442,7 +436,7 @@
</button>
</section>
</div>
<div id="chat" class="panel hide">
<div id="chat" class="panel hidden">
<sm-switch id="is_enter_send_toggle">
<div slot="left" class="flex flex-direction-column">
<h4>Send by Enter</h4>
@ -456,7 +450,7 @@
</div>
</sm-switch>
</div>
<div id="blocked" class="panel hide">
<div id="blocked" class="panel hidden">
<section>
<h4>Blocked</h4>
<ul id="blocked_list" class="observe-empty-state"></ul>
@ -465,7 +459,7 @@
</div>
</section>
</div>
<div id="personalize" class="panel hide">
<div id="personalize" class="panel hidden">
<section>
<h4>Chat preview</h4>
<div id="chat_preview">
@ -489,7 +483,7 @@
<section>
<h4>Set chat and mail background image</h4>
<fieldset id="bg_preview_container" class="flex">
<label id="selected_bg_preview" class="bg-preview hide">
<label id="selected_bg_preview" class="bg-preview hidden">
<input type="radio" name="bg" value="img">
<img src="" alt="background preview" class="bg-preview__image">
</label>
@ -509,7 +503,7 @@
<input type="file" id="select_bg_image" accept="image/*" />
</label>
</section>
<section id="backdrop_options" class="hide">
<section id="backdrop_options" class="hidden">
<h4>App backdrop</h4>
<label class="grid gap-0-3">
<span>Wallpaper dimming</span>
@ -531,7 +525,7 @@
<color-grid id="accent_color_selector"></color-grid>
</section>
</div>
<div id="backup" class="panel hide">
<div id="backup" class="panel hidden">
<section>
<h4>Backup data</h4>
<p>Create a backup of contacts, conversations and mails. Which can later be used to restore
@ -568,14 +562,14 @@
</label>
</section>
</div>
<div id="about" class="panel hide">
<div id="about" class="panel hidden">
<section>
<p>Created by RanchiMall, a Blockchain incorporated entity</p>
</section>
</div>
</div>
</section>
<nav id="main_navbar" class="flex hide">
<nav id="main_navbar" class="flex hidden">
<ul>
<li>
<a id="chat_page_button" class="nav-item flex align-center active" href="#/chat_page" title="Chat">
@ -585,6 +579,12 @@
<path
d="M15 4v7H5.17l-.59.59-.58.58V4h11m1-2H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm5 4h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1z" />
</svg>
<svg class="icon filled" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z" />
</svg>
<span class="nav-item__title">Chat</span>
</a>
</li>
@ -596,6 +596,12 @@
<path
d="M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z" />
</svg>
<svg class="icon filled" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
width="24px" fill="#000000">
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" />
</svg>
<span class="nav-item__title">Mail</span>
</a>
</li>
@ -607,6 +613,14 @@
<path
d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z" />
</svg>
<svg class="icon filled" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<path d="M0,0h24v24H0V0z" fill="none" />
<path
d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z" />
</g>
</svg>
<span class="nav-item__title">Settings</span>
</a>
</li>
@ -649,7 +663,7 @@
<sm-form>
<div id="auto_complete_contact" class="flex flex-direction-column">
<sm-input id="send_mail_to" placeholder="To" animate required></sm-input>
<div id="mail_contact_list" class="hide contact-list"></div>
<div id="mail_contact_list" class="hidden contact-list"></div>
</div>
<sm-input id="subject_of_mail" placeholder="Subject" animate></sm-input>
<sm-textarea id="mail_content" placeholder="Type a mail" name="" id="" rows="10" required></sm-textarea>
@ -699,22 +713,22 @@
<h5 id="flo_id_type">FLO ID</h5>
<sm-copy id="contact_flo_id"></sm-copy>
</div>
<div id="group_description_card" class="hide">
<div id="group_description_card" class="hidden">
<h5>Group description</h5>
<text-field id="group_description"></text-field>
</div>
<fieldset id="contact_options"></fieldset>
</div>
<div id="group_members_card" class="hide">
<div id="group_members_card" class="hidden">
<div class="flex align-center">
<h4 class="h4">Group members</h4>
<button id="edit_group_button" class="button hide justify-right"
<button id="edit_group_button" class="button hidden justify-right"
onclick="editGroupMembers()">Edit</button>
</div>
<p id="group_members_tip" class="tip">Select members to remove or add new
members</p>
<div id="member_options" class="flex hide">
<button id="remove_members_button" class="button button--danger hide"
<div id="member_options" class="flex hidden">
<button id="remove_members_button" class="button button--danger hidden"
onclick="removeGroupMembers()">
Remove selected</button>
<button id="init_add_members_button" class="button" onclick="openPopup('contacts_popup')">Add
@ -738,7 +752,7 @@
</header>
<div class="scrolling-wrapper grid gap-1-5">
<fieldset id="all_contacts_options">
<button id="create_group_option" class="option interactive" onclick="openPopup('group_creation_popup')">
<button id="create_group_option" class="option interactive" onclick="openCreationPopup('group')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
@ -760,6 +774,19 @@
</svg>
Add contact
</button>
<button id="crate_multisig_option" class="option interactive" onclick="openCreationPopup('multisig')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<path
d="M20,9V6h-2v3h-3v2h3v3h2v-3h3V9H20z M9,12c2.21,0,4-1.79,4-4c0-2.21-1.79-4-4-4S5,5.79,5,8C5,10.21,6.79,12,9,12z M9,6 c1.1,0,2,0.9,2,2c0,1.1-0.9,2-2,2S7,9.1,7,8C7,6.9,7.9,6,9,6z M15.39,14.56C13.71,13.7,11.53,13,9,13c-2.53,0-4.71,0.7-6.39,1.56 C1.61,15.07,1,16.1,1,17.22V20h16v-2.78C17,16.1,16.39,15.07,15.39,14.56z M15,18H3v-0.78c0-0.38,0.2-0.72,0.52-0.88 C4.71,15.73,6.63,15,9,15c2.37,0,4.29,0.73,5.48,1.34C14.8,16.5,15,16.84,15,17.22V18z" />
</g>
</svg>
Create multisig address
</button>
</fieldset>
<div class="grid gap-1">
<h5>Contacts</h5>
@ -772,7 +799,7 @@
</div>
</div>
</sm-popup>
<sm-popup id="group_creation_popup">
<sm-popup id="creation_popup">
<header class="grid popup__header" slot="header">
<div class="flex align-center">
<button class="popup__header__close justify-self-start">
@ -783,22 +810,21 @@
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>
</button>
<h4>Create group</h4>
<h4 id="creation_popup__title">Create group</h4>
</div>
</header>
<div id="group_creation_process">
<div id="creation_process">
<div>
<div id="selected_contacts">
<div class="flex align-center space-between">
<h4>Select group members</h4>
<button id="skip_members_button" class="button button--primary"
onclick="showChildElement('group_creation_process',1,{entry: slideInLeft, exit: slideOutLeft})">
<h4>Select members</h4>
<button id="skip_members_button" class="button button--primary hidden">
Skip </button>
</div>
<div id="selected_contacts_container" class="observe-empty-state"></div>
<div class="empty-state">
<p class="warning">*Contacts that haven't yet replied to you, can't be added to a
group. So they won't be visible here.</p>
<p class="info info--warning">*Contacts that haven't yet replied to you, won't be visible here.
</p>
</div>
</div>
<div class="scrolling-wrapper">
@ -808,9 +834,9 @@
</div>
</div>
</div>
<sm-form class="hide">
<sm-form class="hidden">
<button class="button icon-only back-button justify-self-start"
onclick="showChildElement('group_creation_process',0,{entry: slideInRight, exit: slideOutRight})">
onclick="showChildElement('creation_process',0,{entry: slideInRight, exit: slideOutRight})">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
@ -839,6 +865,28 @@
</button>
</div>
</sm-form>
<div class="grid gap-1-5 hidden">
<button class="button icon-only back-button justify-self-start"
onclick="showChildElement('creation_process',0,{entry: slideInRight, exit: slideOutRight})">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
</svg>
</button>
<p id="multisig_creation__warning" class="info info--warning"></p>
<sm-form>
<p>Enter minimum signatures required for approval of a transaction</p>
<sm-input id="min_sign_required" placeholder="Min required" min="1" type="number"
error-text="At least 1 member is required" animate required>
</sm-input>
<div class="multi-state-button">
<button id="create_multisig_button" class="button button--primary" type="submit" disabled>
Create
</button>
</div>
</sm-form>
</div>
</div>
</sm-popup>
@ -859,7 +907,8 @@
<button id="add_members_button" class="button button--primary" disabled>Add</button>
</div>
</header>
<p class="warning">*Contacts that haven't yet replied to you, can't be added to a group. So they won't be
<p class="info info--warning">*Contacts that haven't yet replied to you, can't be added to a group. So they
won't be
visible here.</p>
<div id="popup_contacts_container" class="observe-empty-state"></div>
<div class="empty-state">
@ -1077,18 +1126,18 @@
getRef("group_description").value = description === '' ? 'Add group description' : description;
render.groupMembers(floID)
getRef('contact_details_popup').classList.add('is-group');
getRef('group_members_card').classList.remove('hide')
getRef('group_description_card').classList.remove('hide')
getRef('group_members_card').classList.remove('hidden')
getRef('group_description_card').classList.remove('hidden')
getRef('edit_group_button').dataset.groupId = floID;
getRef('flo_id_type').textContent = 'Group FLO ID'
if (isAdmin) {
getRef('contact_name').disabled = false
getRef('group_description').disabled = false
getRef('edit_group_button').classList.remove('hide')
getRef('edit_group_button').classList.remove('hidden')
} else {
getRef('contact_name').disabled = true
getRef('group_description').disabled = true
getRef('edit_group_button').classList.add('hide')
getRef('edit_group_button').classList.add('hidden')
}
} else {
getRef('flo_id_type').textContent = 'FLO ID'
@ -1096,8 +1145,8 @@
getRef('contact_initial').textContent = getContactName(floID).charAt(0)
getRef("last_interaction_time").textContent = ``;
getRef('contact_details_popup').classList.remove('is-group');
getRef('group_members_card').classList.add('hide')
getRef('group_description_card').classList.add('hide')
getRef('group_members_card').classList.add('hidden')
getRef('group_description_card').classList.add('hidden')
}
getRef('contact_initial').setAttribute('style', `--contact-color: var(${contactColor(floID)})`)
getRef('contact_name').value = getContactName(floID)
@ -1115,9 +1164,14 @@
isRemovingMember = false
break
case 'new_message_popup':
if (Object.keys(floGlobals.contacts).length) {
getRef('search_contacts').classList.remove('hidden')
} else {
getRef('search_contacts').classList.add('hidden')
}
renderContactList(floGlobals.contacts)
break
case 'group_creation_popup':
case 'creation_popup':
const validContacts = []
for (const floID in floGlobals.contacts) {
if (floGlobals.pubKeys.hasOwnProperty(floID)) {
@ -1150,8 +1204,8 @@
isRemovingMember = true
membersToAdd.clear()
break;
case 'group_creation_popup':
showChildElement('group_creation_process', 0)
case 'creation_popup':
showChildElement('creation_process', 0)
renderElem(getRef('select_contacts_container'), html``)
clearAllMembers()
break;
@ -1341,7 +1395,7 @@
}
window.addEventListener('hashchange', e => showPage(window.location.hash))
window.addEventListener("load", () => {
document.body.classList.remove('hide')
document.body.classList.remove('hidden')
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = floCrypto.validateAddr)
document.addEventListener('keyup', (e) => {
if (e.key === 'Escape') {
@ -1481,7 +1535,7 @@
easing: 'ease-out',
})
getRef('chat_view').classList.remove('hide')
getRef('chat_view').classList.remove('hidden')
getRef('chat_view').classList.remove('hide-on-mobile')
getRef('contacts').classList.add('hide-on-mobile')
getRef('main_navbar').classList.add('hide-on-mobile')
@ -1512,7 +1566,7 @@
showChildElement('mail_sections', childIndex)
getRef("mail_type_selector").value = subPageId1
if (subPageId2 && activeMail) {
getRef('mail').classList.remove('hide')
getRef('mail').classList.remove('hidden')
getRef('mail').classList.remove('hide-on-mobile')
getRef('mails').classList.add('hide-on-mobile')
getRef('main_navbar').classList.add('hide-on-mobile')
@ -1547,8 +1601,8 @@
const currentActiveElement = document.querySelector(`.nav-item[href="#/${pageId}"]`)
if (currentActiveElement) {
getRef('main_page').classList.remove('nav-hidden')
if (getRef('main_navbar').classList.contains('hide')) {
getRef('main_navbar').classList.remove('hide-away', 'hide')
if (getRef('main_navbar').classList.contains('hidden')) {
getRef('main_navbar').classList.remove('hide-away', 'hidden')
getRef('main_navbar').animate([
{
transform: isMobileView ? `translateY(100%)` : `translateX(-100%)`,
@ -1603,7 +1657,7 @@
currentActiveElement.classList.add('nav-item--active')
} else {
getRef('main_page').classList.add('nav-hidden')
if (!getRef('main_navbar').classList.contains('hide')) {
if (!getRef('main_navbar').classList.contains('hidden')) {
getRef('main_navbar').classList.add('hide-away')
getRef('main_navbar').animate([
{
@ -1619,14 +1673,14 @@
fill: 'forwards',
easing: 'ease'
}).onfinish = () => {
getRef('main_navbar').classList.add('hide')
getRef('main_navbar').classList.add('hidden')
}
}
}
document.querySelectorAll('.page').forEach(page => page.classList.add('hide'))
getRef(pageId).closest('.page').classList.remove('hide')
document.querySelectorAll('.inner-page').forEach(page => page.classList.add('hide'))
getRef(pageId).classList.remove('hide')
document.querySelectorAll('.page').forEach(page => page.classList.add('hidden'))
getRef(pageId).closest('.page').classList.remove('hidden')
document.querySelectorAll('.inner-page').forEach(page => page.classList.add('hidden'))
getRef(pageId).classList.remove('hidden')
getRef(pageId).animate([
{
opacity: 0,
@ -1912,24 +1966,24 @@
easing: 'ease',
fill: 'forwards'
}
const visibleElement = [...getRef(id).children].find(elem => !elem.classList.contains(mobileView ? 'hide-on-mobile' : 'hide'));
const visibleElement = [...getRef(id).children].find(elem => !elem.classList.contains(mobileView ? 'hide-on-mobile' : 'hidden'));
if (visibleElement === getRef(id).children[index]) return;
visibleElement.getAnimations().forEach(anim => anim.cancel())
getRef(id).children[index].getAnimations().forEach(anim => anim.cancel())
if (visibleElement) {
if (exit) {
visibleElement.animate(exit, animOptions).onfinish = () => {
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hide')
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hide')
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
if (entry)
getRef(id).children[index].animate(entry, animOptions)
}
} else {
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hide')
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hide')
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
}
} else {
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hide')
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
getRef(id).children[index].animate(entry, animOptions)
}
}
@ -1975,11 +2029,8 @@
</script>
<script>
let activeChat = {},
activeMail,
frag = document.createDocumentFragment()
let activeChat = {};
let activeMail;
let renderedDates = {}
let lastSender = ''
@ -2194,7 +2245,7 @@
${initial}
</div>
<h4 class="name wrap-around">${name}</h4>
${isAdmin ? html`<p class="admin-tag">Group admin</p>` : ''}
${isAdmin ? html`<p class="admin-tag">Admin</p>` : ''}
</div>
`)
@ -2405,7 +2456,7 @@
}
} else {
if (floID) {
if(!(floID in messenger.chats))
if (!(floID in messenger.chats))
messenger.addChat(floID)
getRef('chats_list').prepend(html.node`${render.contactCard(floID, { type: 'chat', prepend: true, markUnread: true, ref: getRef('chats_list') })}`)
} else if (groupID) {
@ -2450,7 +2501,7 @@
getRef('search_chats').addEventListener('keyup', e => {
if (e.code === 'ArrowDown') {
for (child of getRef('chats_list').children) {
if (!child.classList.contains('hide')) {
if (!child.classList.contains('hidden')) {
child.focus()
break
}
@ -2458,7 +2509,7 @@
}
if (e.code === 'Enter' && getRef('contacts_container').firstElementChild) {
for (child of getRef('contacts_container').children) {
if (!child.classList.contains('hide')) {
if (!child.classList.contains('hidden')) {
child.click()
break
}
@ -2490,27 +2541,27 @@
}
if (e.code === 'Enter' || e.code === 'Space') {
getRef('send_mail_to').value = document.activeElement.dataset.floId
getRef('mail_contact_list').classList.add('hide')
getRef('mail_contact_list').classList.add('hidden')
}
}
})
getRef('send_mail_to').addEventListener('input', function () {
getRef('mail_contact_list').classList.remove('hide')
getRef('mail_contact_list').classList.remove('hidden')
if (this.value.trim !== '') {
[...getRef('mail_contact_list').children].forEach(child => {
if (getContactName(child.dataset.floId).includes(this.value.trim())) {
child.classList.remove('hide')
child.classList.remove('hidden')
} else {
child.classList.add('hide')
child.classList.add('hidden')
}
})
}
})
getRef('compose_mail_popup').addEventListener('click', e => {
if (e.target.closest('#send_mail_to') || e.target.closest('#mail_contact_list')) {
getRef('mail_contact_list').classList.remove('hide')
getRef('mail_contact_list').classList.remove('hidden')
} else {
getRef('mail_contact_list').classList.add('hide')
getRef('mail_contact_list').classList.add('hidden')
}
})
@ -2529,9 +2580,9 @@
const contacts = getRef('chats_list').querySelectorAll('.contact')
contacts.forEach(child => {
if (`${getContactName(child.dataset.floId)}${child.dataset.floId}`.toLowerCase().includes(this.value.toLowerCase())) {
child.classList.remove('hide')
child.classList.remove('hidden')
} else {
child.classList.add('hide')
child.classList.add('hidden')
}
})
})
@ -2594,7 +2645,7 @@
getRef('mail_contact_list').addEventListener('click', e => {
if (e.target.closest('.contact-list__item')) {
getRef('send_mail_to').value = e.target.closest('.contact-list__item').dataset.floId
getRef('mail_contact_list').classList.add('hide')
getRef('mail_contact_list').classList.add('hidden')
}
})
@ -2635,6 +2686,37 @@
event.preventDefault();
}
function openCreationPopup(type) {
let popupTitle = ''
switch (type) {
case 'group':
popupTitle = 'Create group'
getRef('skip_members_button').classList.remove('hidden')
break;
case 'multisig':
getRef('skip_members_button').classList.add('hidden')
popupTitle = 'Create multisig address'
getRef('multisig_creation__warning').classList.add('hide')
floBlockchainAPI.getBalance(floDapps.user.id).then(balance => {
let warning = `Creation of multisig address consumes FLO, you have ${balance} FLO.`;
getRef('multisig_creation__warning').classList.add('info--warning')
if (balance < 0.2) {
warning = `Creation of multisig address consumes FLO, you don't have enough FLO`;
getRef('multisig_creation__warning').classList.remove('info--warning')
getRef('multisig_creation__warning').classList.add('info--error')
}
getRef('multisig_creation__warning').textContent = warning
getRef('skip_members_button').classList.remove('hidden')
}).catch(err => notify(err, 'error'))
break;
default:
break;
}
getRef('creation_popup__title').textContent = popupTitle
getRef('creation_popup').dataset.type = type
openPopup('creation_popup')
}
getRef('selected_contacts_container').addEventListener('wheel', transformScroll);
getRef('select_contacts_container').addEventListener('change', e => {
@ -2646,17 +2728,23 @@
delegate(getRef('selected_contacts_container'), 'click', '.remove-selected', e => {
removeSelectedContact(e.target.closest('.contact-preview').dataset.floId)
})
const selectedGroupMembers = new Set();
const selectedMembers = new Set();
function checkSelectedMembers() {
if (selectedGroupMembers.size) {
if (selectedMembers.size) {
getRef('skip_members_button').textContent = 'Next'
if (getRef('creation_popup').dataset.type === 'multisig') {
getRef('skip_members_button').classList.remove('hidden')
getRef('min_sign_required').setAttribute('max', selectedMembers.size + 1)
}
} else {
getRef('skip_members_button').textContent = 'Skip'
if (getRef('creation_popup').dataset.type === 'multisig')
getRef('skip_members_button').classList.add('hidden')
}
}
function selectContact(floID) {
if (!selectedGroupMembers.has(floID)) {
selectedGroupMembers.add(floID)
if (!selectedMembers.has(floID)) {
selectedMembers.add(floID)
const name = getContactName(floID)
const preview = html.node`<div class="contact-preview" .dataset="${{ floId: floID }}">
<div class="initial flex align-center" style=${`--contact-color: var(${contactColor(floID)})`}>
@ -2682,7 +2770,7 @@
}
function removeSelectedContact(floID) {
selectedGroupMembers.delete(floID)
selectedMembers.delete(floID)
const relatedContact = getRef('select_contacts_container').querySelector(`[data-flo-id="${floID}"]`)
const relatedPreview = getRef('selected_contacts_container').querySelector(`[data-flo-id="${floID}"]`)
relatedPreview.animate(
@ -2701,9 +2789,39 @@
function clearAllMembers() {
getRef('selected_contacts_container').innerHTML = ''
selectedGroupMembers.clear()
selectedMembers.clear()
checkSelectedMembers()
}
getRef('skip_members_button').addEventListener('click', e => {
if (getRef('creation_popup').dataset.type === 'multisig') {
showChildElement('creation_process', 2, { entry: slideInLeft, exit: slideOutLeft })
} else {
showChildElement('creation_process', 1, { entry: slideInLeft, exit: slideOutLeft })
}
})
getRef('min_sign_required').addEventListener('input', e => {
const { rangeOverflow, rangeUnderflow } = e.target.validity;
if (rangeUnderflow)
e.target.setAttribute('error-text', 'At least 1 member is required ')
if (rangeOverflow)
e.target.setAttribute('error-text', `Maximum ${selectedMembers.size + 1} allowed`)
})
document.getElementById('create_multisig_button').addEventListener('click', () => {
selectedMembers.add(floDapps.user.id)
const selctedPubKeys = [...selectedMembers].map(id => floGlobals.pubKeys[id]);
const minRequired = parseInt(getRef('min_sign_required').value.trim());
buttonLoader('create_multisig_button', true)
messenger.multisig.createAddress(selctedPubKeys, minRequired).then(multisigAddress => {
notify('Created multisig address', 'success');
closePopup();
clearAllMembers();
}).catch(error => notify(error, 'error'))
.finally(() => {
buttonLoader('create_multisig_button', false)
})
})
document.getElementById('create_group_button').addEventListener('click', () => {
const groupName = getRef('group_name_field').value.trim()
@ -2715,8 +2833,8 @@
getRef('chats_list').children[0].click()
closePopup()
notify('Group created', 'success')
if (selectedGroupMembers.size) {
messenger.addGroupMembers(groupInfo.groupID, [...selectedGroupMembers])
if (selectedMembers.size) {
messenger.addGroupMembers(groupInfo.groupID, [...selectedMembers])
.then(res => {
clearAllMembers()
})
@ -2733,12 +2851,12 @@
case 'toggle':
isEmojiPickerOpen = true
getRef('emoji_toggle').classList.toggle('active')
getRef('emoji_picker').classList.toggle('hide')
getRef('emoji_picker').classList.toggle('hidden')
break;
case 'hide':
isEmojiPickerOpen = false
getRef('emoji_toggle').classList.remove('active')
getRef('emoji_picker').classList.add('hide')
getRef('emoji_picker').classList.add('hidden')
break;
}
getRef('scroll_to_bottom').setAttribute('style', `bottom: calc(${window.innerHeight - getRef('chat_footer').getBoundingClientRect().top}px - .5rem)`)
@ -2934,15 +3052,6 @@
}
}
function removeElement(element) {
element.parentNode.removeChild(element);
}
function clearElement(element) {
element.innerHTML = '';
return element;
}
function addContact() {
let floID = getRef('add_contact_floID').value.trim();
let name = getRef('add_contact_name').value.trim();
@ -2972,7 +3081,7 @@
function renderContactList(contactList = {}) {
const contacts = []
for (floID in contactList) {
let isSelected = selectedGroupMembers.has(floID)
let isSelected = selectedMembers.has(floID)
contacts.push(render.contactCard(floID, { type: 'contact', isSelected, ref: getRef('contacts_container') }))
}
renderElem(getRef('contacts_container'), html`${contacts}`)
@ -3106,16 +3215,16 @@
//add prop for previous mail (if available)
getRef("prev_mail").dataset["value"] = prev;
if (prev) {
getRef("prev_mail").classList.remove("hide")
getRef("prev_mail").classList.remove("hidden")
} else {
getRef("prev_mail").classList.add("hide")
getRef("prev_mail").classList.add("hidden")
}
//set values for reply mail form if new view
if (newView) {
getRef('reply_mail_popup').dataset["to"] = (from === myFloID ? to.join(',') : from)
getRef('reply_mail_popup').dataset["prev"] = mailRef;
getRef('subject_of_reply_mail').value = subject.startsWith("Re: ") ? subject : `Re: ${subject}`;
getRef("show_reply_popup").classList.remove("hide");
getRef("show_reply_popup").classList.remove("hidden");
}
messenger.removeMark(mailRef, "unread");
location.hash = `#/mail_page/${getRef("mail_type_selector").value}/mail`
@ -3238,8 +3347,8 @@
document.querySelectorAll('.sidebar-item').forEach(item => item.classList.remove('active'))
document.querySelector(`.sidebar-item[href="#/settings/${subPageId}"]`).classList.add('active')
document.querySelectorAll('.panel').forEach(panel => panel.classList.add('hide'))
getRef(subPageId).classList.remove('hide')
document.querySelectorAll('.panel').forEach(panel => panel.classList.add('hidden'))
getRef(subPageId).classList.remove('hidden')
}
function hidePanel() {
@ -3301,25 +3410,6 @@
})
}
function toggleSearch(target) {
const animOptions = {
duration: 150,
easing: 'ease',
fill: 'forwards'
}
if (getRef(target).classList.contains('hide')) {
getRef(target).classList.remove('hide')
getRef(target).animate(flyInLeft, animOptions).onfinish = () => {
getRef(target).querySelector('sm-input').focusIn()
}
} else {
getRef(target).animate(flyOutLeft, animOptions).onfinish = () => {
getRef(target).classList.add('hide')
getRef(target).querySelector('sm-input').value = ''
}
}
}
function getChatCard(floID) {
return getRef('chats_list').querySelector(`.chat[data-flo-id="${floID}"], .group[data-flo-id="${floID}"]`)
}
@ -3399,7 +3489,7 @@
messenger.rmChat(floGlobals.activeFloID).then(result => {
getChatCard(floGlobals.activeFloID).remove()
closePopup()
getRef('chat_view').classList.add('hide')
getRef('chat_view').classList.add('hidden')
notify('Chat deleted', 'success')
})
}
@ -3415,15 +3505,15 @@
render.groupMembers(groupID)
membersToRemove.clear()
getRef('edit_group_button').textContent = 'Edit';
addClass(['#group_members_tip', '#member_options'], 'hide')
addClass(['#group_members_tip', '#member_options'], 'hidden')
isGroupEditable = false
isRemovingMember = false
} else {
// to-do: make group members selectable except for admin
render.groupMembers(groupID, true)
getRef('edit_group_button').textContent = 'Done'
removeClass(['#group_members_tip', '#member_options', '#init_add_members_button'], 'hide')
getRef('remove_members_button').classList.add('hide')
removeClass(['#group_members_tip', '#member_options', '#init_add_members_button'], 'hidden')
getRef('remove_members_button').classList.add('hidden')
isGroupEditable = true
isRemovingMember = true
}
@ -3441,11 +3531,11 @@
membersToRemove.add(floID)
}
if (membersToRemove.size) {
addClass(['#group_members_tip', '#init_add_members_button'], 'hide')
getRef('remove_members_button').classList.remove('hide')
addClass(['#group_members_tip', '#init_add_members_button'], 'hidden')
getRef('remove_members_button').classList.remove('hidden')
} else {
removeClass(['#group_members_tip', '#init_add_members_button'], 'hide')
getRef('remove_members_button').classList.add('hide')
removeClass(['#group_members_tip', '#init_add_members_button'], 'hidden')
getRef('remove_members_button').classList.add('hidden')
}
}
document.getElementById('popup_contacts_container').addEventListener('change', e => {
@ -3529,11 +3619,11 @@
getRef('select_bg_button').textContent = 'Change background'
getRef('selected_bg_preview').classList.add('bg-preview--selected')
getRef('default_bg_preview').classList.remove('bg-preview--selected')
getRef('backdrop_options').classList.remove('hide')
getRef('backdrop_options').classList.remove('hidden')
} else {
getRef('backdrop_options').classList.add('hide')
getRef('backdrop_options').classList.add('hidden')
}
getRef('selected_bg_preview').classList.remove('hide')
getRef('selected_bg_preview').classList.remove('hidden')
getRef('selected_bg_preview').querySelector('img').src = url
}
const [bgOpacity, bgBlur] = await Promise.all([compactIDB.readData('userSettings', 'bgOpacity'), compactIDB.readData('userSettings', 'bgBlur')])
@ -3560,7 +3650,7 @@
.then(async res => {
getRef('background_image').src = ''
removeClass(['#chat_view', '#mail', '#chat_preview'], 'has-bg-image')
getRef('backdrop_options').classList.add('hide')
getRef('backdrop_options').classList.add('hidden')
})
.catch(err => console.error(err))
}

View File

@ -143,208 +143,208 @@ customElements.define('sm-button',
//Input
const smInput = document.createElement('template')
smInput.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration { display: none; }
input[type=number] {
-moz-appearance:textfield;
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0;
}
input::-ms-reveal,
input::-ms-clear {
display: none;
}
input:invalid{
outline: none;
-webkit-box-shadow: none;
box-shadow: none;
}
::-moz-focus-inner{
border: none;
}
:host{
display: flex;
--success-color: #00C853;
--danger-color: red;
--width: 100%;
--icon-gap: 0.5rem;
--min-height: 3.5rem;
--background: rgba(var(--text-color, (17,17,17)), 0.06);
}
.hide{
display: none !important;
}
button{
display: flex;
border: none;
background: none;
padding: 0;
border-radius: 1rem;
min-width: 0;
cursor: pointer;
}
button:focus{
outline: var(--accent-color, teal) solid medium;
}
.icon {
height: 1.2rem;
width: 1.2rem;
fill: rgba(var(--text-color, (17,17,17)), 0.6);
}
:host(.round) .input{
border-radius: 10rem;
}
.input {
display: flex;
cursor: text;
min-width: 0;
text-align: left;
align-items: center;
position: relative;
gap: var(--icon-gap);
padding: var(--padding, 0.6rem 0.8rem);
border-radius: var(--border-radius,0.3rem);
transition: opacity 0.3s, box-shadow 0.2s;
background: var(--background);
width: 100%;
outline: none;
min-height: var(--min-height);
}
.input.readonly .clear{
opacity: 0 !important;
margin-right: -2rem;
pointer-events: none !important;
}
.readonly{
pointer-events: none;
}
.input:focus-within:not(.readonly){
box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset !important;
}
.disabled{
pointer-events: none;
opacity: 0.6;
}
.label {
grid-area: 1/1/2/2;
font-size: inherit;
opacity: .7;
font-weight: 400;
transition: -webkit-transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s, color .03;
transform-origin: left;
pointer-events: none;
white-space: nowrap;
overflow: hidden;
width: 100%;
user-select: none;
will-change: transform;
}
.outer-container{
position: relative;
width: var(--width);
}
.container{
width: 100%;
display: grid;
grid-template-columns: 1fr auto;
position: relative;
align-items: center;
}
input{
grid-area: 1/1/2/2;
font-size: inherit;
border: none;
background: transparent;
outline: none;
color: inherit;
font-family: inherit;
width: 100%;
caret-color: var(--accent-color, teal);
}
:host([animate]) .input:focus-within .container input,
.animate-placeholder .container input {
-webkit-transform: translateY(0.6rem);
-ms-transform: translateY(0.6rem);
transform: translateY(0.6rem);
<style>
*{
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration { display: none; }
input[type=number] {
-moz-appearance:textfield;
}
:host([animate]) .input:focus-within .label,
.animate-placeholder .label {
-webkit-transform: translateY(-0.7em) scale(0.8);
-ms-transform: translateY(-0.7em) scale(0.8);
transform: translateY(-0.7em) scale(0.8);
opacity: 1;
color: var(--accent-color,teal)
}
:host([variant="outlined"]) .input {
box-shadow: 0 0 0 1px var(--border-color, rgba(var(--text-color, (17,17,17)), 0.3)) inset;
background: rgba(var(--background-color, (255,255,255)), 1);
}
.animate-placeholder:focus-within:not(.readonly) .label{
color: var(--accent-color,teal)
}
.feedback-text:not(:empty){
display: flex;
width: 100%;
text-align: left;
font-size: 0.9rem;
align-items: center;
padding: 0.8rem 0;
color: rgba(var(--text-color, (17,17,17)), 0.8);
}
.success{
color: var(--success-color);
}
.error{
color: var(--danger-color);
}
.status-icon{
margin-right: 0.2rem;
}
.status-icon--error{
fill: var(--danger-color);
}
.status-icon--success{
fill: var(--success-color);
}
@media (any-hover: hover){
.icon:hover{
background: rgba(var(--text-color, (17,17,17)), 0.1);
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0;
}
}
</style>
<div class="outer-container">
<label part="input" class="input">
<slot name="icon"></slot>
<div class="container">
<input type="text"/>
<div part="placeholder" class="label"></div>
<button class="clear hide" title="Clear" tabindex="-1">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12 13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z"/></svg>
</button>
</div>
<slot name="right"></slot>
</label>
<p class="feedback-text"></p>
</div>
`;
input::-ms-reveal,
input::-ms-clear {
display: none;
}
input:invalid{
outline: none;
-webkit-box-shadow: none;
box-shadow: none;
}
::-moz-focus-inner{
border: none;
}
:host{
display: flex;
--success-color: #00C853;
--danger-color: red;
--width: 100%;
--icon-gap: 0.5rem;
--min-height: 3.2rem;
--background: rgba(var(--text-color, (17,17,17)), 0.06);
}
.hide{
display: none !important;
}
button{
display: flex;
border: none;
background: none;
padding: 0;
border-radius: 1rem;
min-width: 0;
cursor: pointer;
}
button:focus{
outline: var(--accent-color, teal) solid medium;
}
.icon {
height: 1.2rem;
width: 1.2rem;
fill: rgba(var(--text-color, (17,17,17)), 0.6);
}
:host(.round) .input{
border-radius: 10rem;
}
.input {
display: flex;
cursor: text;
min-width: 0;
text-align: left;
align-items: center;
position: relative;
gap: var(--icon-gap);
padding: var(--padding, 0.6rem 0.8rem);
border-radius: var(--border-radius,0.3rem);
transition: opacity 0.3s, box-shadow 0.2s;
background: var(--background);
width: 100%;
outline: none;
min-height: var(--min-height);
}
.input.readonly .clear{
opacity: 0 !important;
margin-right: -2rem;
pointer-events: none !important;
}
.readonly{
pointer-events: none;
}
.input:focus-within:not(.readonly){
box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset !important;
}
.disabled{
pointer-events: none;
opacity: 0.6;
}
.label {
grid-area: 1/1/2/2;
font-size: inherit;
opacity: .7;
font-weight: 400;
transition: -webkit-transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s, color .03;
transform-origin: left;
pointer-events: none;
white-space: nowrap;
overflow: hidden;
width: 100%;
user-select: none;
will-change: transform;
}
.outer-container{
position: relative;
width: var(--width);
}
.container{
width: 100%;
display: grid;
grid-template-columns: 1fr auto;
position: relative;
align-items: center;
}
input{
grid-area: 1/1/2/2;
font-size: inherit;
border: none;
background: transparent;
outline: none;
color: inherit;
font-family: inherit;
width: 100%;
caret-color: var(--accent-color, teal);
}
:host([animate]) .input:focus-within .container input,
.animate-placeholder .container input {
-webkit-transform: translateY(0.6rem);
-ms-transform: translateY(0.6rem);
transform: translateY(0.6rem);
}
:host([animate]) .input:focus-within .label,
.animate-placeholder .label {
-webkit-transform: translateY(-0.7em) scale(0.8);
-ms-transform: translateY(-0.7em) scale(0.8);
transform: translateY(-0.7em) scale(0.8);
opacity: 1;
color: var(--accent-color,teal)
}
:host([variant="outlined"]) .input {
box-shadow: 0 0 0 1px var(--border-color, rgba(var(--text-color, (17,17,17)), 0.3)) inset;
background: rgba(var(--background-color, (255,255,255)), 1);
}
.animate-placeholder:focus-within:not(.readonly) .label{
color: var(--accent-color,teal)
}
.feedback-text:not(:empty){
display: flex;
width: 100%;
text-align: left;
font-size: 0.9rem;
align-items: center;
padding: 0.8rem 0;
color: rgba(var(--text-color, (17,17,17)), 0.8);
}
.success{
color: var(--success-color);
}
.error{
color: var(--danger-color);
}
.status-icon{
margin-right: 0.2rem;
}
.status-icon--error{
fill: var(--danger-color);
}
.status-icon--success{
fill: var(--success-color);
}
@media (any-hover: hover){
.icon:hover{
background: rgba(var(--text-color, (17,17,17)), 0.1);
}
}
</style>
<div class="outer-container">
<label part="input" class="input">
<slot name="icon"></slot>
<div class="container">
<input type="text"/>
<div part="placeholder" class="label"></div>
<button class="clear hide" title="Clear" tabindex="-1">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.414L9.172 7.757 7.757 9.172 10.586 12l-2.829 2.828 1.415 1.415L12 13.414l2.828 2.829 1.415-1.415L13.414 12l2.829-2.828-1.415-1.415L12 10.586z"/></svg>
</button>
</div>
<slot name="right"></slot>
</label>
<p class="feedback-text"></p>
</div>
`;
customElements.define('sm-input',
class extends HTMLElement {
@ -372,6 +372,7 @@ customElements.define('sm-input',
this.focusOut = this.focusOut.bind(this);
this.fireEvent = this.fireEvent.bind(this);
this.checkInput = this.checkInput.bind(this);
this.handleKeydown = this.handleKeydown.bind(this);
this.vibrate = this.vibrate.bind(this);
}
@ -384,9 +385,9 @@ customElements.define('sm-input',
}
set value(val) {
if (val === this.input.value) return;
this.input.value = val;
this.checkInput();
this.fireEvent();
}
get placeholder() {
@ -453,9 +454,9 @@ customElements.define('sm-input',
this.feedbackText.classList.add('error');
this.feedbackText.classList.remove('success');
this.feedbackText.innerHTML = `
<svg class="status-icon status-icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>
${this._errorText}
`;
<svg class="status-icon status-icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>
${this._errorText}
`;
}
}
return (_isValid && _customValid);
@ -508,6 +509,15 @@ customElements.define('sm-input',
this.feedbackText.textContent = '';
}
}
handleKeydown(e) {
if (e.key.length === 1) {
if (!['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'].includes(e.key)) {
e.preventDefault();
} else if (e.key === '.' && e.target.value.includes('.')) {
e.preventDefault();
}
}
}
vibrate() {
this.outerContainer.animate([
{ transform: 'translateX(-1rem)' },
@ -549,6 +559,10 @@ customElements.define('sm-input',
else if (name === 'type') {
if (this.hasAttribute('type') && this.getAttribute('type') === 'number') {
this.input.setAttribute('inputmode', 'decimal');
this.input.addEventListener('keydown', this.handleKeydown);
} else {
this.input.removeEventListener('keydown', this.handleKeydown);
}
}
else if (name === 'helper-text') {
@ -586,117 +600,118 @@ customElements.define('sm-input',
disconnectedCallback() {
this.input.removeEventListener('input', this.checkInput);
this.clearBtn.removeEventListener('click', this.clear);
this.input.removeEventListener('keydown', this.handleKeydown);
}
})
//textarea
const smTextarea = document.createElement('template')
smTextarea.innerHTML = `
<style>
*,
*::before,
*::after {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
::-moz-focus-inner{
border: none;
}
.hide{
opacity: 0 !important;
}
:host{
display: grid;
--danger-color: red;
--background: rgba(var(--text-color,(17,17,17)), 0.06);
--padding: initial;
--max-height: 8rem;
}
:host([variant="outlined"]) .textarea {
box-shadow: 0 0 0 0.1rem rgba(var(--text-color,(17,17,17)), 0.4) inset;
background: rgba(var(--background-color,(255,255,255)), 1);
}
.textarea{
display: grid;
position: relative;
cursor: text;
min-width: 0;
text-align: left;
overflow: hidden auto;
grid-template-columns: 1fr;
align-items: stretch;
max-height: var(--max-height);
background: var(--background);
border-radius: var(--border-radius, 0.3rem);
padding: var(--padding);
}
.textarea::after,
textarea{
padding: 0.7rem 1rem;
width: 100%;
min-width: 1em;
font: inherit;
color: inherit;
resize: none;
grid-area: 2/1;
justify-self: stretch;
background: none;
appearance: none;
border: none;
outline: none;
line-height: 1.5;
}
.textarea::after{
content: attr(data-value) ' ';
visibility: hidden;
white-space: pre-wrap;
overflow-wrap: break-word;
word-wrap: break-word;
hyphens: auto;
overflow: hidden;
}
.readonly{
pointer-events: none;
}
.textarea:focus-within:not(.readonly){
box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset;
}
.placeholder{
position: absolute;
margin: 0.7rem 1rem;
opacity: .7;
font-weight: inherit;
font-size: inherit;
line-height: 1.5;
pointer-events: none;
user-select: none;
}
:host([disabled]) .textarea{
cursor: not-allowed;
opacity: 0.6;
}
@media (any-hover: hover){
::-webkit-scrollbar{
width: 0.5rem;
height: 0.5rem;
<style>
*,
*::before,
*::after {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
::-moz-focus-inner{
border: none;
}
::-webkit-scrollbar-thumb{
background: rgba(var(--text-color,(17,17,17)), 0.3);
border-radius: 1rem;
&:hover{
background: rgba(var(--text-color,(17,17,17)), 0.5);
.hide{
opacity: 0 !important;
}
:host{
display: grid;
--danger-color: red;
--border-radius: 0.3rem;
--background: rgba(var(--text-color,(17,17,17)), 0.06);
--padding: initial;
--max-height: 8rem;
}
:host([variant="outlined"]) .textarea {
box-shadow: 0 0 0 0.1rem rgba(var(--text-color,(17,17,17)), 0.4) inset;
background: rgba(var(--background-color,(255,255,255)), 1);
}
.textarea{
display: grid;
position: relative;
cursor: text;
min-width: 0;
text-align: left;
overflow: hidden auto;
grid-template-columns: 1fr;
align-items: stretch;
max-height: var(--max-height);
background: var(--background);
border-radius: var(--border-radius);
padding: var(--padding);
}
.textarea::after,
textarea{
padding: 0.7rem 1rem;
width: 100%;
min-width: 1em;
font: inherit;
color: inherit;
resize: none;
grid-area: 2/1;
justify-self: stretch;
background: none;
appearance: none;
border: none;
outline: none;
line-height: 1.5;
overflow: hidden;
}
.textarea::after{
content: attr(data-value) ' ';
visibility: hidden;
white-space: pre-wrap;
overflow-wrap: break-word;
word-wrap: break-word;
hyphens: auto;
}
.readonly{
pointer-events: none;
}
.textarea:focus-within:not(.readonly){
box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset;
}
.placeholder{
position: absolute;
margin: 0.7rem 1rem;
opacity: .7;
font-weight: inherit;
font-size: inherit;
line-height: 1.5;
pointer-events: none;
user-select: none;
}
:host([disabled]) .textarea{
cursor: not-allowed;
opacity: 0.6;
}
@media (any-hover: hover){
::-webkit-scrollbar{
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb{
background: rgba(var(--text-color,(17,17,17)), 0.3);
border-radius: 1rem;
&:hover{
background: rgba(var(--text-color,(17,17,17)), 0.5);
}
}
}
}
</style>
<label class="textarea" part="textarea">
<span class="placeholder"></span>
<textarea rows="1"></textarea>
</label>
`;
</style>
<label class="textarea" part="textarea">
<span class="placeholder"></span>
<textarea rows="1"></textarea>
</label>
`;
customElements.define('sm-textarea',
class extends HTMLElement {
constructor() {
@ -788,26 +803,26 @@ customElements.define('sm-textarea',
})
const smForm = document.createElement('template');
smForm.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
:host{
display: flex;
width: 100%;
}
form{
display: grid;
gap: var(--gap, 1.5rem);
width: 100%;
}
</style>
<form part="form" onsubmit="return false">
<slot></slot>
</form>
`;
<style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
:host{
display: grid;
width: 100%;
}
form{
display: inherit;
gap: var(--gap, 1.5rem);
width: 100%;
}
</style>
<form part="form" onsubmit="return false">
<slot></slot>
</form>
`;
customElements.define('sm-form', class extends HTMLElement {
constructor() {
@ -821,7 +836,7 @@ customElements.define('sm-form', class extends HTMLElement {
this.requiredElements
this.submitButton
this.resetButton
this.allRequiredValid = false;
this.invalidFields = false;
this.debounce = this.debounce.bind(this)
this._checkValidity = this._checkValidity.bind(this)
@ -839,18 +854,13 @@ customElements.define('sm-form', class extends HTMLElement {
};
}
_checkValidity() {
this.allRequiredValid = this.requiredElements.every(elem => elem.isValid)
if (!this.submitButton) return;
if (this.allRequiredValid) {
this.submitButton.disabled = false;
}
else {
this.submitButton.disabled = true;
}
this.invalidFields = this.requiredElements.filter(elem => !elem.isValid)
this.submitButton.disabled = this.invalidFields.length;
}
handleKeydown(e) {
if (e.key === 'Enter' && !e.target.tagName.includes('TEXTAREA')) {
if (this.allRequiredValid) {
if (e.key === 'Enter' && e.target.tagName.includes('SM-INPUT')) {
if (!this.invalidFields.length) {
if (this.submitButton) {
this.submitButton.click()
}
@ -858,9 +868,8 @@ customElements.define('sm-form', class extends HTMLElement {
bubbles: true,
composed: true,
}))
}
else {
this.requiredElements.find(elem => !elem.isValid).vibrate()
} else {
this.requiredElements.forEach(elem => { if (!elem.isValid) elem.vibrate() })
}
}
}
@ -878,14 +887,22 @@ customElements.define('sm-form', class extends HTMLElement {
this._checkValidity()
}
connectedCallback() {
const slot = this.shadowRoot.querySelector('slot')
slot.addEventListener('slotchange', this.elementsChanged)
this.shadowRoot.querySelector('slot').addEventListener('slotchange', this.elementsChanged)
this.addEventListener('input', this.debounce(this._checkValidity, 100));
this.addEventListener('keydown', this.debounce(this.handleKeydown, 100));
const mutationObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
this.elementsChanged()
}
})
})
mutationObserver.observe(this, { childList: true, subtree: true })
}
disconnectedCallback() {
this.removeEventListener('input', this.debounce(this._checkValidity, 100));
this.removeEventListener('keydown', this.debounce(this.handleKeydown, 100));
mutationObserver.disconnect()
}
})
//switch
@ -1134,7 +1151,7 @@ smSelect.innerHTML = `
display: -webkit-box;
display: -ms-flexbox;
display: flex;
--min-width: 100%;
--min-width: max-content;
}
:host([disabled]) .select{
opacity: 0.6;
@ -1175,7 +1192,7 @@ smSelect.innerHTML = `
grid-template-columns: 1fr auto;
grid-template-areas: 'heading heading' '. .';
padding: var(--padding,0.6rem 0.8rem);
background: rgba(var(--text-color,(17,17,17)), 0.06);
background: var(--background, rgba(var(--text-color,(17,17,17)), 0.06));
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
@ -1215,7 +1232,7 @@ smSelect.innerHTML = `
-webkit-box-shadow: 0.4rem 0.8rem 1.2rem #00000030;
box-shadow: 0.4rem 0.8rem 1.2rem #00000030;
}
.rotate{
:host([open]) .toggle-icon{
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg)
@ -1241,7 +1258,7 @@ smSelect.innerHTML = `
<div class="select">
<div class="selection">
<div class="selected-option-text"></div>
<svg class="icon toggle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 13.172l4.95-4.95 1.414 1.414L12 16 5.636 9.636 7.05 8.222z"/></svg>
<svg class="icon toggle-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 13.172l4.95-4.95 1.414 1.414L12 16 5.636 9.636 7.05 8.222z"/></svg>
</div>
<div part="options" class="options hide">
<slot></slot>
@ -1263,9 +1280,11 @@ customElements.define('sm-select', class extends HTMLElement {
this.handleOptionSelection = this.handleOptionSelection.bind(this)
this.handleKeydown = this.handleKeydown.bind(this)
this.handleClickOutside = this.handleClickOutside.bind(this)
this.selectOption = this.selectOption.bind(this)
this.availableOptions
this.previousOption
this._value = undefined;
this.isOpen = false;
this.label = ''
this.slideDown = [{
@ -1293,7 +1312,6 @@ customElements.define('sm-select', class extends HTMLElement {
}
this.optionList = this.shadowRoot.querySelector('.options')
this.chevron = this.shadowRoot.querySelector('.toggle')
this.selection = this.shadowRoot.querySelector('.selection')
this.selectedOptionText = this.shadowRoot.querySelector('.selected-option-text')
}
@ -1307,12 +1325,7 @@ customElements.define('sm-select', class extends HTMLElement {
const selectedOption = this.availableOptions.find(option => option.getAttribute('value') === val)
if (selectedOption) {
this.setAttribute('value', val)
this.selectedOptionText.textContent = `${this.label}${selectedOption.textContent}`;
if (this.previousOption) {
this.previousOption.classList.remove('check-selected')
}
selectedOption.classList.add('check-selected')
this.previousOption = selectedOption
this.selectOption(selectedOption)
} else {
console.warn(`There is no option with ${val} as value`)
}
@ -1320,19 +1333,25 @@ customElements.define('sm-select', class extends HTMLElement {
reset(fire = true) {
if (this.availableOptions[0] && this.previousOption !== this.availableOptions[0]) {
const firstElement = this.availableOptions[0];
if (this.previousOption) {
this.previousOption.classList.remove('check-selected')
}
firstElement.classList.add('check-selected')
this.value = firstElement.getAttribute('value')
this.selectedOptionText.textContent = `${this.label}${firstElement.textContent}`
this.previousOption = firstElement;
const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0];
this.value = selectedOption.getAttribute('value')
if (fire) {
this.fireEvent()
}
}
}
selectOption(selectedOption) {
if (this.previousOption) {
this.previousOption.classList.remove('check-selected')
this.previousOption.removeAttribute('selected')
}
if (this.previousOption !== selectedOption) {
selectedOption.classList.add('check-selected')
selectedOption.setAttribute('selected', '')
this.selectedOptionText.textContent = `${this.label}${selectedOption.textContent}`;
this.previousOption = selectedOption
}
}
focusIn() {
this.selection.focus()
@ -1341,11 +1360,11 @@ customElements.define('sm-select', class extends HTMLElement {
open() {
this.optionList.classList.remove('hide')
this.optionList.animate(this.slideDown, this.animationOptions)
this.chevron.classList.add('rotate')
this.setAttribute('open', '')
this.isOpen = true
}
collapse() {
this.chevron.classList.remove('rotate')
this.removeAttribute('open')
this.optionList.animate(this.slideUp, this.animationOptions)
.onfinish = () => {
this.optionList.classList.add('hide')
@ -1437,6 +1456,11 @@ customElements.define('sm-select', class extends HTMLElement {
let slot = this.shadowRoot.querySelector('slot')
slot.addEventListener('slotchange', e => {
this.availableOptions = slot.assignedElements()
this.availableOptions.forEach(elem => {
if (elem.hasAttribute('selected')) {
this._value = elem.value;
}
});
this.reset(false)
});
this.addEventListener('click', this.handleClick)