diff --git a/Resources/Public/Styles/Login.css b/Resources/Public/Styles/Login.css
index bf9034a..28d9b6f 100644
--- a/Resources/Public/Styles/Login.css
+++ b/Resources/Public/Styles/Login.css
@@ -16,6 +16,7 @@
}
.neos-two-factor__secret-wrapper {
+ /* specificity hack */
display: flex !important;
}
@@ -46,12 +47,19 @@
}
.neos-two-factor__secret__copy__button {
+ /* specificity hack */
display: flex !important;
gap: 8px;
align-items: center;
justify-content: center;
}
+.neos-two-factor__secret__copy__button span,
+.neos-two-factor__secret__copy__button span i {
+ display: flex;
+ align-items: center;
+}
+
.neos-two-factor__secret__copy__button svg {
height: 16px;
width: 16px;
@@ -61,6 +69,7 @@
}
.neos-two-factor__hidden {
+ /* specificity hack */
display: none !important;
}
@@ -69,6 +78,17 @@
display: block;
width: 100%;
overflow: hidden;
+
+ font-size: 14px;
+ line-height: 1.6em;
+}
+
+.neos-two-factor__secret div,
+.neos-two-factor__secret p,
+.neos-two-factor__secret svg {
+ box-sizing: content-box;
+ margin: 0 !important;
+ padding: 0 !important;
}
.neos-two-factor__secret p {
@@ -76,7 +96,10 @@
color: #0f0f0f;
font-family: monospace;
- letter-spacing: 2px;
+}
+
+.neos-two-factor__secret span:nth-child(3n) {
+ margin-right: 4px;
}
.neos-two-factor__secret .neos-two-factor__secret__number {
@@ -88,7 +111,7 @@
position: absolute;
top: 0;
width: 10em;
- height: 100%;
+ height: 1.6em;
display: flex;
align-items: center;
@@ -98,9 +121,9 @@
.neos-two-factor__secret__overflow-indicator--left svg,
.neos-two-factor__secret__overflow-indicator--right svg {
- height: 18px;
+ height: 1.2em;
- fill: #1a1a1a;
+ fill: #3f3f3f;
}
.neos-two-factor__secret__overflow-indicator--left {
diff --git a/Resources/Public/index.js b/Resources/Public/index.js
index 646cccb..4d1d930 100644
--- a/Resources/Public/index.js
+++ b/Resources/Public/index.js
@@ -1,63 +1,83 @@
+// Progressively enhance the secret form element on load
window.addEventListener('load', function () {
document.querySelectorAll('.neos-two-factor__secret-wrapper')
- .forEach(function (secretFormElement) {
- const secretInput = secretFormElement.querySelector('input#secret')
- const secretDialog = secretFormElement.querySelector('dialog')
- const showSecretButton = secretFormElement.querySelector('.neos-two-factor__secret__show__button')
- const secretDialogCloseButton = secretDialog.querySelector('.neos-two-factor__secret__close__button')
-
- showSecretButton.onclick = function () {
- secretDialog.showModal()
- }
-
- secretDialogCloseButton.onclick = function () {
- secretDialog.close()
- }
-
- const secretDisplay = secretDialog.querySelector('.neos-two-factor__secret')
- const allChars = Array.from(secretDisplay.querySelectorAll('span'))
- const firstChar = allChars[0]
- const lastChar = allChars[allChars.length - 1]
- const overflowIndicatorLeft = secretDialog.querySelector('.neos-two-factor__secret__overflow-indicator--left')
- const overflowIndicatorRight = secretDialog.querySelector('.neos-two-factor__secret__overflow-indicator--right')
-
- const intersectionObserverFirstChar = new IntersectionObserver(function (entries) {
- if (entries[0].isIntersecting) {
- overflowIndicatorLeft.classList.add('neos-two-factor__hidden')
- } else {
- overflowIndicatorLeft.classList.remove('neos-two-factor__hidden')
- }
+ .forEach(progressivelyEnhanceSecretFormElement)
+})
+
+function progressivelyEnhanceSecretFormElement(secretFormElement) {
+ // Collect inner elements (scoped to the secretFormElement)
+ const secretInput = secretFormElement.querySelector('input#secret')
+ const secretDialog = secretFormElement.querySelector('dialog')
+ const showSecretButton = secretFormElement.querySelector('.neos-two-factor__secret__show__button')
+ const secretDialogCloseButton = secretDialog.querySelector('.neos-two-factor__secret__close__button')
+ const secretDisplay = secretDialog.querySelector('.neos-two-factor__secret')
+
+ // Init secret modal buttons
+ showSecretButton.onclick = function () {
+ secretDialog.showModal()
+ }
+
+ secretDialogCloseButton.onclick = function () {
+ secretDialog.close()
+ }
+
+ // Init overflow indicators
+ // Each character are wrapped in a span element (so we can have different styles for numbers and letters)
+ const allCharElements = Array.from(secretDisplay.querySelectorAll('span'))
+ const firstCharElement = allCharElements[0]
+ const lastCharElement = allCharElements[allCharElements.length - 1]
+
+ const overflowIndicatorLeft = secretDialog.querySelector('.neos-two-factor__secret__overflow-indicator--left')
+ const overflowIndicatorRight = secretDialog.querySelector('.neos-two-factor__secret__overflow-indicator--right')
+
+ const intersectionObserverOptions = {
+ threshold: 0.9
+ }
+
+ const firstCharIntersectionObserver = new IntersectionObserver(function (entries) {
+ // Hide or show indicator when first character is visible or not visible respectively
+ if (entries[0].isIntersecting) {
+ overflowIndicatorLeft.classList.add('neos-two-factor__hidden')
+ } else {
+ overflowIndicatorLeft.classList.remove('neos-two-factor__hidden')
+ }
+ }, intersectionObserverOptions)
+
+ const lastCharIntersectionObserver = new IntersectionObserver(function (entries) {
+ // Hide or show indicator when last character is visible or not visible respectively
+ if (entries[0].isIntersecting) {
+ overflowIndicatorRight.classList.add('neos-two-factor__hidden')
+ } else {
+ overflowIndicatorRight.classList.remove('neos-two-factor__hidden')
+ }
+ }, intersectionObserverOptions)
+
+ firstCharIntersectionObserver.observe(firstCharElement)
+ lastCharIntersectionObserver.observe(lastCharElement)
+
+ // Init copy secret button
+ const copySecretButton = secretFormElement.querySelector('.neos-two-factor__secret__copy__button')
+ copySecretButton.onclick = async function () {
+ try {
+ // Copy secret to clipboard
+ await navigator.clipboard.writeText(secretInput.value)
+ // Disable button and show success indicator
+ copySecretButton.setAttribute('disabled', 'disabled')
+ copySecretButton.querySelectorAll('span').forEach(element => {
+ element.classList.toggle('neos-two-factor__hidden')
})
- const intersectionObserverLastChar = new IntersectionObserver(function (entries) {
- if (entries[0].isIntersecting) {
- overflowIndicatorRight.classList.add('neos-two-factor__hidden')
- } else {
- overflowIndicatorRight.classList.remove('neos-two-factor__hidden')
- }
+ // Wait for 1 second
+ await new Promise(function (resolve) {
+ setTimeout(resolve, 1000)
})
- intersectionObserverFirstChar.observe(firstChar)
- intersectionObserverLastChar.observe(lastChar)
-
- const copySecretButton = secretFormElement.querySelector('.neos-two-factor__secret__copy__button')
- copySecretButton.onclick = function () {
-
- navigator.clipboard.writeText(secretInput.value)
- .then(function () {
- copySecretButton.querySelectorAll('span').forEach(element => {
- element.classList.toggle('neos-two-factor__hidden')
- })
-
- return new Promise(function (resolve) {
- setTimeout(resolve, 1000)
- })
- })
- .then(function () {
- copySecretButton.querySelectorAll('span').forEach(element => {
- element.classList.toggle('neos-two-factor__hidden')
- })
- })
- }
- })
-})
+ } finally {
+ // Re-enable button and hide success indicator
+ copySecretButton.removeAttribute('disabled')
+ copySecretButton.querySelectorAll('span').forEach(element => {
+ element.classList.toggle('neos-two-factor__hidden')
+ })
+ }
+ }
+}