updated password input UI

This commit is contained in:
Danny Coates 2018-02-16 12:56:53 -08:00
parent 8d41111cd6
commit 346e604f34
No known key found for this signature in database
GPG key ID: 4C442633C62E00CB
37 changed files with 282 additions and 288 deletions

View file

@ -1,29 +0,0 @@
.changePasswordSection {
padding: 10px 0;
align-self: left;
max-width: 100%;
overflow-wrap: break-word;
}
.btn--reset {
width: 80px;
height: 30px;
background: #fff;
border-color: rgba(12, 12, 13, 0.3);
margin-top: 5px;
margin-left: 15px;
margin-bottom: 12px;
line-height: 24px;
color: #313131;
}
.btn--reset:hover {
background: #efeff1;
}
@media (max-device-width: 520px), (max-width: 520px) {
.changePasswordSection {
align-self: center;
min-width: 95%;
}
}

View file

@ -1,52 +0,0 @@
const html = require('choo/html');
const raw = require('choo/html/raw');
const passwordInput = require('../passwordInput');
module.exports = function(state, emit) {
const file = state.storage.getFileById(state.params.id);
return html`<div class="changePasswordSection">
${passwordSpan(file.password)}
<button
class="btn btn--reset"
onclick=${toggleResetInput}
>${state.translate('changePasswordButton')}</button>
${passwordInput(
state.translate('unlockInputPlaceholder'),
state.translate('changePasswordButton'),
changePassword
)}
</div>`;
function passwordSpan(password) {
password = password || '●●●●●';
const span = html`<span>${raw(
state.translate('passwordResult', {
password: '<pre class="passwordMask"></pre>'
})
)}</span>`;
const masked = span.querySelector('.passwordMask');
masked.textContent = password.replace(/./g, '●');
return span;
}
function changePassword(event) {
event.preventDefault();
const password = document.getElementById('password-input').value;
if (password.length > 0) {
emit('password', { password, file });
}
return false;
}
function toggleResetInput(event) {
const form = event.target.parentElement.querySelector('form.passwordInput');
const input = document.getElementById('password-input');
if (form.style.visibility === 'hidden' || form.style.visibility === '') {
form.style.visibility = 'visible';
input.focus();
} else {
form.style.visibility = 'hidden';
}
}
};

View file

@ -1,15 +1,13 @@
const html = require('choo/html');
module.exports = function(state, emit) {
return html`
<button class="btn btn--download"
onclick=${download}>${state.translate('downloadButtonLabel')}
</button>`;
function download(event) {
event.preventDefault();
emit('download', state.fileInfo);
}
return html`
<div>
<button class="btn btn--download"
onclick=${download}>${state.translate('downloadButtonLabel')}
</button>
</div>`;
};

View file

@ -1,7 +1,7 @@
.fileData {
font-size: 15px;
vertical-align: top;
color: #4a4a4a;
color: var(--lightTextColor);
padding: 17px 19px 0;
line-height: 23px;
position: relative;

View file

@ -3,30 +3,13 @@ const assets = require('../../../common/assets');
const number = require('../../utils').number;
const deletePopup = require('../popup');
function timeLeft(milliseconds, state) {
const minutes = Math.floor(milliseconds / 1000 / 60);
const hours = Math.floor(minutes / 60);
if (hours >= 1) {
return state.translate('expiresHoursMinutes', {
hours,
minutes: minutes % 60
});
} else if (hours === 0) {
if (minutes === 0) {
return state.translate('expiresMinutes', { minutes: '< 1' });
}
return state.translate('expiresMinutes', { minutes });
}
return null;
}
module.exports = function(file, state, emit) {
const ttl = file.expiresAt - Date.now();
const remainingTime =
timeLeft(ttl, state) || state.translate('linkExpiredAlt');
const downloadLimit = file.dlimit || 1;
const totalDownloads = file.dtotal || 0;
const row = html`
return html`
<tr id="${file.id}">
<td class="fileData fileData--overflow" title="${file.name}">
<a class="link" href="/share/${file.id}">${file.name}</a>
@ -84,6 +67,21 @@ module.exports = function(file, state, emit) {
emit('delete', { file, location: 'upload-list' });
emit('render');
}
return row;
};
function timeLeft(milliseconds, state) {
const minutes = Math.floor(milliseconds / 1000 / 60);
const hours = Math.floor(minutes / 60);
if (hours >= 1) {
return state.translate('expiresHoursMinutes', {
hours,
minutes: minutes % 60
});
} else if (hours === 0) {
if (minutes === 0) {
return state.translate('expiresMinutes', { minutes: '< 1' });
}
return state.translate('expiresMinutes', { minutes });
}
return null;
}

View file

@ -7,7 +7,7 @@
.fileList__header {
font-size: 16px;
color: #858585;
color: var(--lightTextColor);
font-weight: lighter;
text-align: left;
background: rgba(0, 148, 251, 0.05);

View file

@ -2,9 +2,8 @@ const html = require('choo/html');
const file = require('../file');
module.exports = function(state, emit) {
let table = '';
if (state.storage.files.length) {
table = html`
return html`
<table class="fileList">
<thead>
<tr>
@ -31,5 +30,4 @@ module.exports = function(state, emit) {
</table>
`;
}
return table;
};

View file

@ -20,7 +20,7 @@
}
.legalSection__link {
color: #858585;
color: var(--lightTextColor);
opacity: 0.9;
white-space: nowrap;
margin-right: 2vw;
@ -31,7 +31,7 @@
}
.legalSection__link:visited {
color: #858585;
color: var(--lightTextColor);
}
.legalSection__mozLogo {

View file

@ -2,9 +2,6 @@ const html = require('choo/html');
const assets = require('../../../common/assets');
module.exports = function(state, emit) {
function clicked() {
emit('experiment', { cd3: 'promo' });
}
let classes = 'fxPromo';
switch (state.promo) {
case 'blue':
@ -30,4 +27,8 @@ module.exports = function(state, emit) {
>Download Firefox now </a></span>
</div>
</div>`;
function clicked() {
emit('experiment', { cd3: 'promo' });
}
};

View file

@ -59,7 +59,7 @@
border-radius: 3px;
border: 1px solid var(--primaryControlBGColor);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
color: #fff;
color: var(--primaryControlFGColor);
cursor: pointer;
display: block;
float: right;
@ -85,7 +85,7 @@
}
.feedback:active {
background-color: #0277d8;
background-color: var(--primaryControlHoverColor);
}
@media (max-device-width: 520px), (max-width: 520px) {

View file

@ -11,6 +11,28 @@ const assets = require('../../../common/assets');
string with the value from package.json. 🤢
*/
const version = require('../../../package.json').version || 'VERSION';
const browser = browserName();
module.exports = function(state) {
return html`<header class="header">
<div class="logo">
<a class="logo__link" href="/">
<img
src="${assets.get('send_logo.svg')}"
alt="Send"/>
<h1 class="logo__title">Send</h1>
</a>
<div class="logo__subtitle">
<a class="logo__subtitle-link" href="https://testpilot.firefox.com">Firefox Test Pilot</a>
<div>${state.translate('siteSubtitle')}</div>
</div>
</div>
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}"
rel="noreferrer noopener"
class="feedback"
target="_blank">${state.translate('siteFeedback')}</a>
</header>`;
};
function browserName() {
try {
@ -34,26 +56,3 @@ function browserName() {
return 'unknown';
}
}
const browser = browserName();
module.exports = function(state) {
return html`<header class="header">
<div class="logo">
<a class="logo__link" href="/">
<img
src="${assets.get('send_logo.svg')}"
alt="Send"/>
<h1 class="logo__title">Send</h1>
</a>
<div class="logo__subtitle">
<a class="logo__subtitle-link" href="https://testpilot.firefox.com">Firefox Test Pilot</a>
<div>${state.translate('siteSubtitle')}</div>
</div>
</div>
<a href="https://qsurvey.mozilla.com/s3/txp-firefox-send?ver=${version}&browser=${browser}"
rel="noreferrer noopener"
class="feedback"
target="_blank">${state.translate('siteFeedback')}</a>
</header>`;
};

View file

@ -1,26 +1,54 @@
const html = require('choo/html');
module.exports = function(placeholder, action, submit) {
module.exports = function(file, state, emit) {
const setting = state.settingPassword;
const formClass = file.hasPassword
? 'passwordInput'
: 'passwordInput passwordInput--hidden';
const inputClass = setting ? 'input input--copied' : 'input input--noBtn';
const btnClass = setting
? 'inputBtn inputBtn--loading'
: 'inputBtn inputBtn--hidden';
const action = file.hasPassword
? state.translate('changePasswordButton')
: state.translate('addPasswordButton');
return html`
<div>
<form
class="passwordInput passwordInput--hidden"
onsubmit=${submit}
class="${formClass}"
onsubmit=${setPassword}
data-no-csrf>
<input id="password-input"
class="input input--noBtn"
${setting ? 'disabled' : ''}
class="${inputClass}"
maxlength="32"
autocomplete="off"
type="password"
oninput=${inputChanged}
placeholder="${placeholder}">
placeholder="${
file.hasPassword
? passwordPlaceholder(file.password)
: state.translate('unlockInputPlaceholder')
}">
<input type="submit"
id="password-btn"
class="inputBtn inputBtn--hidden"
value="${action}"/>
${setting ? 'disabled' : ''}
class="${btnClass}"
value="${setting ? '' : action}">
</form>
<div class="passwordInput__msg">${message(
setting,
file.hasPassword,
state.translate('passwordIsSet')
)}</div>
</div>
`;
function inputChanged() {
const pwdmsg = document.querySelector('.passwordInput__msg');
if (pwdmsg) {
pwdmsg.textContent = '';
}
const resetInput = document.getElementById('password-input');
const resetBtn = document.getElementById('password-btn');
if (resetInput.value.length > 0) {
@ -31,4 +59,24 @@ module.exports = function(placeholder, action, submit) {
resetInput.classList.add('input--noBtn');
}
}
function setPassword(event) {
event.preventDefault();
const password = document.getElementById('password-input').value;
if (password.length > 0) {
emit('password', { password, file });
}
return false;
}
};
function passwordPlaceholder(password) {
return password ? password.replace(/./g, '●') : '●●●●●●●●●●●●';
}
function message(setting, pwd, deflt) {
if (setting || !pwd) {
return '';
}
return deflt;
}

View file

@ -3,16 +3,42 @@
display: flex;
flex-wrap: nowrap;
width: 80%;
padding: 10px 5px;
padding: 10px 5px 5px;
}
.passwordInput__msg {
height: 100px;
margin: 0 5px;
font-size: 15px;
color: var(--lightTextColor);
}
.passwordInput--hidden {
visibility: hidden;
}
.inputBtn--loading {
background-image: url('../assets/spinner.svg');
background-position: center;
background-size: 30px 30px;
background-repeat: no-repeat;
background-color: var(--successControlBGColor);
border: 1px solid var(--successControlBGColor);
color: var(--successControlFGColor);
width: 10em;
}
.inputBtn--loading:hover {
background-color: var(--successControlBGColor);
}
@media (max-device-width: 520px), (max-width: 520px) {
.passwordInput {
flex-direction: column;
width: inherit;
}
.inputBtn--loading {
width: inherit;
}
}

View file

@ -1,14 +1,6 @@
const html = require('choo/html');
module.exports = function(msg, confirmText, cancelText, confirmCallback) {
function hide(e) {
e.stopPropagation();
const popup = document.querySelector('.popup.popup--show');
if (popup) {
popup.classList.remove('popup--show');
}
}
return html`
<div class="popup__wrapper">
<div class="popup" onblur=${hide} tabindex="-1">
@ -23,4 +15,12 @@ module.exports = function(msg, confirmText, cancelText, confirmCallback) {
</div>
</div>
</div>`;
function hide(e) {
e.stopPropagation();
const popup = document.querySelector('.popup.popup--show');
if (popup) {
popup.classList.remove('popup--show');
}
}
};

View file

@ -2,8 +2,8 @@
visibility: hidden;
min-width: 204px;
min-height: 105px;
background-color: #fff;
color: #000;
background-color: var(--pageBGColor);
color: var(--textColor);
border: 1px solid #d7d7db;
padding: 15px 24px;
box-sizing: content-box;
@ -82,7 +82,7 @@
}
.popup__yes {
color: #fff;
color: var(--primaryControlFGColor);
background-color: var(--primaryControlBGColor);
border-radius: 5px;
padding: 5px 25px;

View file

@ -9,7 +9,7 @@ const circumference = 2 * Math.PI * radius;
module.exports = function(progressRatio) {
const dashOffset = (1 - progressRatio) * circumference;
const percentComplete = percent(progressRatio);
const div = html`
return html`
<div class="progress">
<svg
width="${oDiameter}"
@ -37,5 +37,4 @@ module.exports = function(progressRatio) {
</svg>
</div>
`;
return div;
};

View file

@ -5,6 +5,25 @@ module.exports = function(selected, options, translate, changed) {
const id = `select-${Math.random()}`;
let x = selected;
return html`
<div class="selectbox">
<div onclick=${toggle}>
<span class="link">${translate(selected)}</span>
<svg width="32" height="32">
<polygon points="8 18 17 28 26 18" fill="#0094fb"/>
</svg>
</div>
<ul id="${id}" class="selectbox__options">
${options.map(
i => html`
<li
class="selectbox__option"
onclick=${choose}
data-value="${i}">${number(i)}</li>`
)}
</ul>
</div>`;
function close() {
const ul = document.getElementById(id);
const body = document.querySelector('body');
@ -37,21 +56,4 @@ module.exports = function(selected, options, translate, changed) {
}
close();
}
return html`
<div class="selectbox">
<div onclick=${toggle}>
<span class="link">${translate(selected)}</span>
<svg width="32" height="32">
<polygon points="8 18 17 28 26 18" fill="#0094fb"/>
</svg>
</div>
<ul id="${id}" class="selectbox__options">
${options.map(
i =>
html`<li class="selectbox__option" onclick=${choose} data-value="${i}">${number(
i
)}</li>`
)}
</ul>
</div>`;
};

View file

@ -15,7 +15,7 @@
left: 0;
padding: 0;
margin: 40px 0;
background-color: white;
background-color: var(--pageBGColor);
border: 1px solid rgba(12, 12, 13, 0.3);
border-radius: 4px;
box-shadow: 1px 2px 4px rgba(12, 12, 13, 0.3);

View file

@ -3,10 +3,13 @@ const passwordInput = require('../passwordInput');
module.exports = function(state, emit) {
const file = state.storage.getFileById(state.params.id);
const div = html`
return html`
<div class="setPasswordSection">
<div class="checkbox">
<input
${file.hasPassword ? 'disabled' : ''}
${file.hasPassword ? 'checked' : ''}
class="checkbox__input"
id="add-password"
type="checkbox"
@ -16,22 +19,9 @@ module.exports = function(state, emit) {
${state.translate('requirePasswordCheckbox')}
</label>
</div>
${passwordInput(
state.translate('unlockInputPlaceholder'),
state.translate('addPasswordButton'),
addPassword
)}
${passwordInput(file, state, emit)}
</div>`;
function addPassword(event) {
event.preventDefault();
const password = document.getElementById('password-input').value;
if (password.length > 0) {
emit('password', { password, file });
}
return false;
}
function togglePasswordInput(e) {
const unlockInput = document.getElementById('password-input');
const boxChecked = e.target.checked;
@ -44,6 +34,4 @@ module.exports = function(state, emit) {
unlockInput.value = '';
}
}
return div;
};

View file

@ -11,7 +11,7 @@
.checkbox__input {
position: absolute;
visibility: collapse;
opacity: 0;
}
.checkbox__label {
@ -31,12 +31,13 @@
border-radius: 2px;
}
.checkbox__input:focus + .checkbox__label::before,
.checkbox:hover .checkbox__label::before {
border: 1px solid var(--primaryControlBGColor);
}
.checkbox__input:checked + .checkbox__label {
color: #000;
color: var(--textColor);
}
.checkbox__input:checked + .checkbox__label::before {
@ -44,6 +45,19 @@
background-position: 2px 1px;
}
.checkbox__input:disabled + .checkbox__label {
cursor: auto;
}
.checkbox__input:disabled + .checkbox__label::before {
background-image: url('../assets/check-16.svg');
background-repeat: no-repeat;
background-size: 18px 18px;
border-color: var(--successControlBGColor);
background-color: var(--successControlBGColor);
cursor: auto;
}
@media (max-device-width: 520px), (max-width: 520px) {
.setPasswordSection {
align-self: center;