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.classList.add(`${NAME}-active`)
el.dispatchEvent(new Event(`${NAME}-ready`))
})
}

View File

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

View File

@ -2,226 +2,246 @@
@import "~bootstrap/scss/mixins";
@mixin hover-disabled() {
&:not(.disabled) {
&.active,
&:active,
&:hover,
&:focus {
@content;
}
/// Remove the unit of a length
/// @param {Number} $number - Number to remove unit from
/// @return {Number} - Unitless number
@function strip-unit($number) {
@if type-of($number) == 'number' and not unitless($number) {
@return $number / ($number * 0 + 1);
}
@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: "") {
content: $content;
display: $display;
position: $pos;
position: $pos;
display: $display;
content: $content;
}
@mixin responsive-ratio($x, $y, $pseudo: false) {
$padding: unquote(math.div($y, $x) * 100 + "%");
$padding: unquote(math.div($y, $x) * 100 + "%");
@if $pseudo {
&:before {
@include pseudo($pos: relative);
padding-top: $padding;
width: 100%;
@if $pseudo {
&:before {
width: 100%;
padding-top: $padding;
@include pseudo($pos: relative);
}
} @else {
padding-top: $padding;
}
} @else {
padding-top: $padding;
}
}
@mixin input-placeholder {
&.placeholder {
@content;
}
&.placeholder {
@content;
}
&:-moz-placeholder {
@content;
}
&:-moz-placeholder {
@content;
}
&::-moz-placeholder {
@content;
}
&::-moz-placeholder {
@content;
}
&:-ms-input-placeholder {
@content;
}
&:-ms-input-placeholder {
@content;
}
&::-webkit-input-placeholder {
@content;
}
&::-webkit-input-placeholder {
@content;
}
}
@mixin truncate($truncation-boundary) {
max-width: $truncation-boundary;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
max-width: $truncation-boundary;
white-space: nowrap;
text-overflow: ellipsis;
}
@mixin fix-bold() {
display: inline-flex;
flex-direction: column;
display: inline-flex;
flex-direction: column;
&:before {
content: attr(data-text);
content: attr(data-text) / "";
font-weight: bold;
height: 0;
overflow: hidden;
pointer-events: none;
user-select: none;
visibility: hidden;
}
&:before {
font-weight: bold;
visibility: hidden;
overflow: hidden;
height: 0;
content: attr(data-text);
content: attr(data-text) / "";
user-select: none;
pointer-events: none;
}
}
@mixin dropdowns-dark() {
.navbar-dark {
.nav-link {
@include hover-focus {
background: $navbar-dark-hover-background;
}
.navbar-dark {
.nav-link {
@include hover-focus {
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,
.nav-link.active {
background: $navbar-dark-active-background;
.dropdown-menu.bg-dark {
border-color: $dark;
.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() {
.dropdown.show {
.dropdown {
&:hover,
&.active,
&:focus {
> .dropdown-menu {
display: block;
.dropdown.show {
.dropdown {
&:hover,
&.active,
&:focus {
> .dropdown-menu {
display: block;
}
}
}
}
}
}
@media only screen and (min-width: map-get($grid-breakpoints, "md")) {
.dropdown-hover ul li {
position: relative;
}
.dropdown-hover ul li {
&:hover,
&.active,
&:focus {
> .dropdown-toggle::after {
transform: rotate(-90deg);
@media only screen and (min-width: map-get($grid-breakpoints, "md")) {
.dropdown-hover ul li {
position: relative;
}
> ul {
display: block;
.dropdown-hover ul li {
&:hover,
&.active,
&:focus {
> .dropdown-toggle::after {
transform: rotate(-90deg);
}
> ul {
display: block;
}
}
}
}
}
.dropdown-hover ul ul {
display: none;
left: 0;
min-width: 250px;
position: absolute;
top: 100%;
}
.dropdown-hover ul ul li {
position: relative;
}
.dropdown-hover ul ul li {
&:hover,
&.active,
&:focus {
> ul {
display: block;
.dropdown-hover ul ul {
position: absolute;
top: 100%;
left: 0;
display: none;
min-width: 250px;
}
}
}
.dropdown-hover ul ul ul {
display: none;
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 li {
position: relative;
}
}
}
.dropdown-hover ul ul ul ul {
display: none;
left: -100%;
min-width: 250px;
position: absolute;
top: 0;
z-index: 1;
.dropdown-hover ul ul li {
&:hover,
&.active,
&:focus {
> ul {
display: block;
}
}
}
.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,
xxxxl: 1836px,
) !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-base: "Roboto", $font-family-sans-serif !default;
$html-font-size: 14px!default;
$spacer: 1rem !default;
$headings-margin-bottom: $spacer !default;
$enable-rounded: false !default;
$enable-shadows: true !default;
$enable-gradients: false !default;
$enable-transitions: true !default;
$enable-reduced-motion: true !default;
$enable-caret: false !default;
$enable-grid-classes: true !default;
$enable-button-pointers: true !default;
$enable-rfs: true !default;
$enable-validation-icons: true !default;
$enable-negative-margins: true !default;
$enable-deprecation-messages: true !default;
$enable-important-utilities: true !default;
$breadcrumb-divider: quote("/") !default;
// ui framework settings
$body-gutter-x: $spacer !default;
$body-gutter-y: $spacer !default;
$body-double-gutter-x: $body-gutter-x * 2 !default;
$body-double-gutter-y: $body-gutter-y * 2 !default;
$body-gutter-reduced-x: $body-gutter-x * 0.5 !default;
$body-gutter-reduced-y: $body-gutter-y * 0.5 !default;
$body-gutter-reduced-d-x: $body-gutter-x * 0.25 !default;
$body-gutter-reduced-d-y: $body-gutter-y * 0.25 !default;
$body-gutter-reduced-x: $body-gutter-x * .5 !default;
$body-gutter-reduced-y: $body-gutter-y * .5 !default;
$body-gutter-reduced-d-x: $body-gutter-x * .25 !default;
$body-gutter-reduced-d-y: $body-gutter-y * .25 !default;
$form-spacer-x: $spacer !default;
$form-spacer-y: $spacer !default;
$element-spacer-x: 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-y: map.get($spacers, 2) !default;
$element-reduced-d-spacer-x: map.get($spacers, 1) !default;
$element-reduced-d-spacer-y: map.get($spacers, 1) !default;
$body-bg: #fff !default;
$body-color: #212529 !default;
// site specific variables
$extra-large-screen: 2000px !default;
$full-body-min-width: map-get($grid-breakpoints, "lg") !default;
$typography-breakpoint: map-get($grid-breakpoints, "sm") - 1 !default;
$header-bg: var(--bs-dark) !default;
$header-color: var(--bs-light) !default;
$header-link: var(--bs-white) !default;
$main-nav-link-color: var(--bs-white) !default;
$main-nav-link-bg: none !default;
$main-nav-toggler-size: 2rem !default;
$main-nav-link-hover-bg: none !default;
$main-nav-link-hover-color: var(--bs-cyan) !default;
$main-nav-dropdown-bg: $header-bg !default;
$main-nav-dropdown-color: $header-link !default;
$main-nav-dropdown-hover-bg: var(--bs-black) !default;
$main-nav-dropdown-hover-color: $main-nav-link-hover-color !default;
$sidebar-nav-link-hover-color: var(--bs-indigo) !default;
$footer-bg: $header-bg !default;
$footer-color: $header-color !default;
$footer-link: $header-link !default;
$footer-footer-bg: $main-nav-dropdown-hover-bg !default;
$sliderelement-carousel-slide-max-y: 70vh !default;
$sliderelement-carousel-slide-bg: $header-bg !default;
$sliderelement-carousel-slide-ratio-x: 16 !default;
$sliderelement-carousel-slide-ratio-y: 9 !default;
$carousel-title-color: $white !default;
$carousel-slide-min-height: 4rem !default;
$carousel-text-shadow: 1px 1px $black !default;
$carousel-controls-font-size: 3rem;
$carousel-controls-zindex: 11 !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;
$breadcrumb-active-color: $sidebar-nav-link-hover-color;
$breadcrumb-active-color: $sidebar-nav-link-hover-color;