UI: captcha on window load and on AJAX

This commit is contained in:
Tony Air 2022-09-12 22:07:08 +02:00
parent 5f54ca9d6a
commit 35bd02ac50
5 changed files with 291 additions and 232 deletions

74
src/js/ui/captcha.js Normal file
View File

@ -0,0 +1,74 @@
import Events from '../_events'
const CaptchaUI = ((window) => {
const NAME = 'js-captcha'
const init = () => {
console.log(`${NAME}: init`)
const submitListener = (e) => {
console.log(`${NAME}: Validating Captcha ...`)
const field = e.currentTarget.querySelectorAll(`.${NAME}, .g-recaptcha`).forEach((el) => {
grecaptcha.execute(el.dataset.widgetid)
})
}
const attachCaptcha = () => {
console.log(`${NAME}: Rendering Captcha ...`)
const fields = document.querySelectorAll(`.${NAME}, .g-recaptcha`)
const grecaptcha = window.grecaptcha
fields.forEach((el, i) => {
if (el.dataset.widgetid || el.innerHTML !== '') {
// already initialized
return
}
const form = el.closest(form)
const widgetid = grecaptcha.render(el, el.dataset)
el.dataset.widgetid = widgetid
if(el.dataset.size === 'invisible' && !el.dataset.callback){
grecaptcha.execute(widgetid)
form.addEventListener('submit', submitListener)
}
el.classList.add(`${NAME}-active`)
el.dispatchEvent(new Event(`${NAME}-ready`))
})
}
const loadScript = (callback) => {
if(typeof window.grecaptcha !== 'undefined'){
callback()
}
console.log(`${NAME}: Loading Captcha API ...`)
const script = document.createElement('script');
script.id = 'captchaAPI';
script.src = 'https://www.google.com/recaptcha/api.js?render=explicit&hl=' + document.querySelector('html').getAttribute('lang').substr(0,2)
script.async = true
script.onload = function() {
console.log(`${NAME}: Captcha API is loaded.`)
callback()
}
document.body.append(script)
}
if(document.querySelectorAll('.g-recaptcha').length){
loadScript(attachCaptcha);
}else{
console.log(`${NAME}: No Captcha fields.`)
}
window.noCaptchaFieldRender = attachCaptcha
}
window.addEventListener(`${Events.LODEDANDREADY}`, init)
window.addEventListener(`${Events.AJAX}`, init)
})(window)
export default CaptchaUI

View File

@ -187,6 +187,7 @@ const CarouselUI = ((window) => {
el.dataset.ui = el.ui el.dataset.ui = el.ui
el.classList.add(`${NAME}-active`) el.classList.add(`${NAME}-active`)
el.dispatchEvent(new Event(`${NAME}-ready`))
}) })
} }

View File

@ -22,22 +22,22 @@ const DropdownHoverUI = ((window) => {
const Toggle = (el) => { const Toggle = (el) => {
HideAll() HideAll()
if (el.querySelector('.dropdown-menu').classList.contains('show')) { el.querySelector('.dropdown-menu').classList.toggle('show')
Hide(el)
} else {
Show(el)
}
} }
const Show = (el) => { const Show = (e) => {
e.stopPropagation()
const el = e.currentTarget
el.classList.add(...ACTIVECLS) el.classList.add(...ACTIVECLS)
el.closest('.dropdown').classList.add(...ACTIVECLS)
el.querySelector('.dropdown-menu').classList.add('show') el.querySelector('.dropdown-menu').classList.add('show')
} }
const Hide = (el) => { const Hide = (e) => {
e.stopPropagation()
const el = e.currentTarget
el.classList.remove(...ACTIVECLS) el.classList.remove(...ACTIVECLS)
el.closest('.dropdown').classList.remove(...ACTIVECLS)
el.querySelector('.dropdown-menu').classList.remove('show') el.querySelector('.dropdown-menu').classList.remove('show')
} }
@ -48,14 +48,8 @@ const DropdownHoverUI = ((window) => {
const hoverableEls = document.querySelectorAll('[data-bs-toggle="hover"]') const hoverableEls = document.querySelectorAll('[data-bs-toggle="hover"]')
const attachHoverEvents = (el) => { const attachHoverEvents = (el) => {
el.addEventListener('mouseover', (e) => { el.addEventListener('mouseover', Show, false)
e.stopPropagation() el.addEventListener('mouseleave', Hide, false)
Show(e.currentTarget)
}, false)
el.addEventListener('mouseleave', (e) => {
e.stopPropagation()
Hide(e.currentTarget)
}, false)
el.classList.add(`${NAME}-active`) el.classList.add(`${NAME}-active`)
} }

View File

@ -2,226 +2,246 @@
@import "~bootstrap/scss/mixins"; @import "~bootstrap/scss/mixins";
@mixin hover-disabled() { /// Remove the unit of a length
&:not(.disabled) { /// @param {Number} $number - Number to remove unit from
&.active, /// @return {Number} - Unitless number
&:active, @function strip-unit($number) {
&:hover, @if type-of($number) == 'number' and not unitless($number) {
&:focus { @return $number / ($number * 0 + 1);
@content;
}
} }
@return $number;
}
@function rem($pxValue) {
@return #{strip-unit($pxValue) / strip-unit($html-font-size)}rem;
}
/*.component {
font-size: rem(14px); // or rem(14)
}*/
@mixin hover-disabled() {
&:not(.disabled) {
&.active,
&:active,
&:hover,
&:focus {
@content;
}
}
} }
@mixin pseudo($display: block, $pos: absolute, $content: "") { @mixin pseudo($display: block, $pos: absolute, $content: "") {
content: $content; position: $pos;
display: $display; display: $display;
position: $pos; content: $content;
} }
@mixin responsive-ratio($x, $y, $pseudo: false) { @mixin responsive-ratio($x, $y, $pseudo: false) {
$padding: unquote(math.div($y, $x) * 100 + "%"); $padding: unquote(math.div($y, $x) * 100 + "%");
@if $pseudo { @if $pseudo {
&:before { &:before {
@include pseudo($pos: relative); width: 100%;
padding-top: $padding; padding-top: $padding;
width: 100%;
@include pseudo($pos: relative);
}
} @else {
padding-top: $padding;
} }
} @else {
padding-top: $padding;
}
} }
@mixin input-placeholder { @mixin input-placeholder {
&.placeholder { &.placeholder {
@content; @content;
} }
&:-moz-placeholder { &:-moz-placeholder {
@content; @content;
} }
&::-moz-placeholder { &::-moz-placeholder {
@content; @content;
} }
&:-ms-input-placeholder { &:-ms-input-placeholder {
@content; @content;
} }
&::-webkit-input-placeholder { &::-webkit-input-placeholder {
@content; @content;
} }
} }
@mixin truncate($truncation-boundary) { @mixin truncate($truncation-boundary) {
max-width: $truncation-boundary; overflow: hidden;
overflow: hidden; max-width: $truncation-boundary;
text-overflow: ellipsis; white-space: nowrap;
white-space: nowrap; text-overflow: ellipsis;
} }
@mixin fix-bold() { @mixin fix-bold() {
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
&:before { &:before {
content: attr(data-text); font-weight: bold;
content: attr(data-text) / ""; visibility: hidden;
font-weight: bold; overflow: hidden;
height: 0; height: 0;
overflow: hidden; content: attr(data-text);
pointer-events: none; content: attr(data-text) / "";
user-select: none; user-select: none;
visibility: hidden; pointer-events: none;
} }
} }
@mixin dropdowns-dark() { @mixin dropdowns-dark() {
.navbar-dark { .navbar-dark {
.nav-link { .nav-link {
@include hover-focus { @include hover-focus {
background: $navbar-dark-hover-background; background: $navbar-dark-hover-background;
} }
}
.active > .nav-link,
.nav-link.active {
background: $navbar-dark-active-background;
}
.nav-link.show,
.navbar-nav .show > .nav-link {
color: $navbar-dark-show-color;
background: $navbar-dark-show-background;
}
} }
.active > .nav-link, .dropdown-menu.bg-dark {
.nav-link.active { border-color: $dark;
background: $navbar-dark-active-background;
.nav-link {
color: $navbar-dark-color;
@include hover-focus {
color: $navbar-dark-hover-color;
}
&.disabled {
cursor: default;
color: $navbar-dark-disabled-color;
}
}
.show > .nav-link,
.active > .nav-link,
.nav-link.show,
.nav-link.active {
color: $navbar-dark-active-color;
}
.dropdown-item {
@include hover-focus {
color: $navbar-dark-hover-color;
background: $navbar-dark-hover-background;
}
&.active,
&:active {
background: $navbar-dark-active-background;
}
.nav-link {
background: none;
}
}
} }
.nav-link.show,
.navbar-nav .show > .nav-link {
background: $navbar-dark-show-background;
color: $navbar-dark-show-color;
}
}
.dropdown-menu.bg-dark {
border-color: $dark;
.nav-link {
color: $navbar-dark-color;
@include hover-focus {
color: $navbar-dark-hover-color;
}
&.disabled {
color: $navbar-dark-disabled-color;
cursor: default;
}
}
.show > .nav-link,
.active > .nav-link,
.nav-link.show,
.nav-link.active {
color: $navbar-dark-active-color;
}
.dropdown-item {
@include hover-focus {
background: $navbar-dark-hover-background;
color: $navbar-dark-hover-color;
}
&.active,
&:active {
background: $navbar-dark-active-background;
}
.nav-link {
background: none;
}
}
}
} }
@mixin dropdown-hovers() { @mixin dropdown-hovers() {
.dropdown.show { .dropdown.show {
.dropdown { .dropdown {
&:hover, &:hover,
&.active, &.active,
&:focus { &:focus {
> .dropdown-menu { > .dropdown-menu {
display: block; display: block;
}
}
} }
}
}
}
@media only screen and (min-width: map-get($grid-breakpoints, "md")) {
.dropdown-hover ul li {
position: relative;
} }
.dropdown-hover ul li { @media only screen and (min-width: map-get($grid-breakpoints, "md")) {
&:hover, .dropdown-hover ul li {
&.active, position: relative;
&:focus {
> .dropdown-toggle::after {
transform: rotate(-90deg);
} }
> ul { .dropdown-hover ul li {
display: block; &:hover,
&.active,
&:focus {
> .dropdown-toggle::after {
transform: rotate(-90deg);
}
> ul {
display: block;
}
}
} }
}
}
.dropdown-hover ul ul { .dropdown-hover ul ul {
display: none; position: absolute;
left: 0; top: 100%;
min-width: 250px; left: 0;
position: absolute; display: none;
top: 100%; min-width: 250px;
}
.dropdown-hover ul ul li {
position: relative;
}
.dropdown-hover ul ul li {
&:hover,
&.active,
&:focus {
> ul {
display: block;
} }
}
}
.dropdown-hover ul ul ul { .dropdown-hover ul ul li {
display: none; position: relative;
left: 100%;
min-width: 250px;
position: absolute;
top: 0;
}
.dropdown-hover ul ul ul li {
position: relative;
}
.dropdown-hover ul ul ul li {
&:hover,
&.active,
&:focus {
ul {
display: block;
} }
}
}
.dropdown-hover ul ul ul ul { .dropdown-hover ul ul li {
display: none; &:hover,
left: -100%; &.active,
min-width: 250px; &:focus {
position: absolute; > ul {
top: 0; display: block;
z-index: 1; }
}
}
.dropdown-hover ul ul ul {
position: absolute;
top: 0;
left: 100%;
display: none;
min-width: 250px;
}
.dropdown-hover ul ul ul li {
position: relative;
}
.dropdown-hover ul ul ul li {
&:hover,
&.active,
&:focus {
ul {
display: block;
}
}
}
.dropdown-hover ul ul ul ul {
position: absolute;
z-index: 1;
top: 0;
left: -100%;
display: none;
min-width: 250px;
}
} }
}
} }

View File

@ -27,108 +27,78 @@ $container-max-widths: (
xxxl: 1536px, xxxl: 1536px,
xxxxl: 1836px, xxxxl: 1836px,
) !default; ) !default;
$font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default; $font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default; $font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
$font-family-base: "Roboto", $font-family-sans-serif !default; $font-family-base: "Roboto", $font-family-sans-serif !default;
$html-font-size: 14px!default;
$spacer: 1rem !default; $spacer: 1rem !default;
$headings-margin-bottom: $spacer !default; $headings-margin-bottom: $spacer !default;
$enable-rounded: false !default; $enable-rounded: false !default;
$enable-shadows: true !default; $enable-shadows: true !default;
$enable-gradients: false !default; $enable-gradients: false !default;
$enable-transitions: true !default; $enable-transitions: true !default;
$enable-reduced-motion: true !default; $enable-reduced-motion: true !default;
$enable-caret: false !default; $enable-caret: false !default;
$enable-grid-classes: true !default; $enable-grid-classes: true !default;
$enable-button-pointers: true !default; $enable-button-pointers: true !default;
$enable-rfs: true !default; $enable-rfs: true !default;
$enable-validation-icons: true !default; $enable-validation-icons: true !default;
$enable-negative-margins: true !default; $enable-negative-margins: true !default;
$enable-deprecation-messages: true !default; $enable-deprecation-messages: true !default;
$enable-important-utilities: true !default; $enable-important-utilities: true !default;
$breadcrumb-divider: quote("/") !default; $breadcrumb-divider: quote("/") !default;
// ui framework settings // ui framework settings
$body-gutter-x: $spacer !default; $body-gutter-x: $spacer !default;
$body-gutter-y: $spacer !default; $body-gutter-y: $spacer !default;
$body-double-gutter-x: $body-gutter-x * 2 !default; $body-double-gutter-x: $body-gutter-x * 2 !default;
$body-double-gutter-y: $body-gutter-y * 2 !default; $body-double-gutter-y: $body-gutter-y * 2 !default;
$body-gutter-reduced-x: $body-gutter-x * .5 !default;
$body-gutter-reduced-x: $body-gutter-x * 0.5 !default; $body-gutter-reduced-y: $body-gutter-y * .5 !default;
$body-gutter-reduced-y: $body-gutter-y * 0.5 !default; $body-gutter-reduced-d-x: $body-gutter-x * .25 !default;
$body-gutter-reduced-d-y: $body-gutter-y * .25 !default;
$body-gutter-reduced-d-x: $body-gutter-x * 0.25 !default;
$body-gutter-reduced-d-y: $body-gutter-y * 0.25 !default;
$form-spacer-x: $spacer !default; $form-spacer-x: $spacer !default;
$form-spacer-y: $spacer !default; $form-spacer-y: $spacer !default;
$element-spacer-x: map.get($spacers, 3) !default; $element-spacer-x: map.get($spacers, 3) !default;
$element-spacer-y: map.get($spacers, 3) !default; $element-spacer-y: map.get($spacers, 3) !default;
$element-reduced-spacer-x: map.get($spacers, 2) !default; $element-reduced-spacer-x: map.get($spacers, 2) !default;
$element-reduced-spacer-y: map.get($spacers, 2) !default; $element-reduced-spacer-y: map.get($spacers, 2) !default;
$element-reduced-d-spacer-x: map.get($spacers, 1) !default; $element-reduced-d-spacer-x: map.get($spacers, 1) !default;
$element-reduced-d-spacer-y: map.get($spacers, 1) !default; $element-reduced-d-spacer-y: map.get($spacers, 1) !default;
$body-bg: #fff !default; $body-bg: #fff !default;
$body-color: #212529 !default; $body-color: #212529 !default;
// site specific variables // site specific variables
$extra-large-screen: 2000px !default; $extra-large-screen: 2000px !default;
$full-body-min-width: map-get($grid-breakpoints, "lg") !default; $full-body-min-width: map-get($grid-breakpoints, "lg") !default;
$typography-breakpoint: map-get($grid-breakpoints, "sm") - 1 !default; $typography-breakpoint: map-get($grid-breakpoints, "sm") - 1 !default;
$header-bg: var(--bs-dark) !default; $header-bg: var(--bs-dark) !default;
$header-color: var(--bs-light) !default; $header-color: var(--bs-light) !default;
$header-link: var(--bs-white) !default; $header-link: var(--bs-white) !default;
$main-nav-link-color: var(--bs-white) !default; $main-nav-link-color: var(--bs-white) !default;
$main-nav-link-bg: none !default; $main-nav-link-bg: none !default;
$main-nav-toggler-size: 2rem !default; $main-nav-toggler-size: 2rem !default;
$main-nav-link-hover-bg: none !default; $main-nav-link-hover-bg: none !default;
$main-nav-link-hover-color: var(--bs-cyan) !default; $main-nav-link-hover-color: var(--bs-cyan) !default;
$main-nav-dropdown-bg: $header-bg !default; $main-nav-dropdown-bg: $header-bg !default;
$main-nav-dropdown-color: $header-link !default; $main-nav-dropdown-color: $header-link !default;
$main-nav-dropdown-hover-bg: var(--bs-black) !default; $main-nav-dropdown-hover-bg: var(--bs-black) !default;
$main-nav-dropdown-hover-color: $main-nav-link-hover-color !default; $main-nav-dropdown-hover-color: $main-nav-link-hover-color !default;
$sidebar-nav-link-hover-color: var(--bs-indigo) !default; $sidebar-nav-link-hover-color: var(--bs-indigo) !default;
$footer-bg: $header-bg !default; $footer-bg: $header-bg !default;
$footer-color: $header-color !default; $footer-color: $header-color !default;
$footer-link: $header-link !default; $footer-link: $header-link !default;
$footer-footer-bg: $main-nav-dropdown-hover-bg !default; $footer-footer-bg: $main-nav-dropdown-hover-bg !default;
$sliderelement-carousel-slide-max-y: 70vh !default; $sliderelement-carousel-slide-max-y: 70vh !default;
$sliderelement-carousel-slide-bg: $header-bg !default; $sliderelement-carousel-slide-bg: $header-bg !default;
$sliderelement-carousel-slide-ratio-x: 16 !default; $sliderelement-carousel-slide-ratio-x: 16 !default;
$sliderelement-carousel-slide-ratio-y: 9 !default; $sliderelement-carousel-slide-ratio-y: 9 !default;
$carousel-title-color: $white !default; $carousel-title-color: $white !default;
$carousel-slide-min-height: 4rem !default; $carousel-slide-min-height: 4rem !default;
$carousel-text-shadow: 1px 1px $black !default; $carousel-text-shadow: 1px 1px $black !default;
$carousel-controls-font-size: 3rem; $carousel-controls-font-size: 3rem;
$carousel-controls-zindex: 11 !default; $carousel-controls-zindex: 11 !default;
$carousel-controls-shadow: 1px 1px $black !default; $carousel-controls-shadow: 1px 1px $black !default;
$carousel-controls-hover-bg: transparentize($black, 0.4) !default; $carousel-controls-hover-bg: transparentize($black, .4) !default;
$carousel-slide-img-loading-max-height: 70vh !default; $carousel-slide-img-loading-max-height: 70vh !default;
$breadcrumb-active-color: $sidebar-nav-link-hover-color;
$breadcrumb-active-color: $sidebar-nav-link-hover-color;