feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
1
modules/backend/formwidgets/sensitive/assets/js/dist/sensitive.js
vendored
Normal file
1
modules/backend/formwidgets/sensitive/assets/js/dist/sensitive.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunk_wintercms_wn_backend_module=self.webpackChunk_wintercms_wn_backend_module||[]).push([[794],{915:function(t,e,i){var n=i(591),s=i.n(n),o=i(969),a={insert:"head",singleton:!1};s()(o.A,a),o.A.locals;(t=>{class e extends t.PluginBase{construct(t){this.element=t,this.config=this.snowboard.dataConfig(this,t),this.clean=Boolean(t.dataset.clean),this.hidden=!0,this.input=t.querySelector("[data-input]"),this.toggle=t.querySelector("[data-toggle]"),this.icon=t.querySelector("[data-icon]"),this.loader=t.querySelector("[data-loader]"),this.copy=t.querySelector("[data-copy]"),this.events={input:()=>this.onInput(),toggle:()=>this.onToggle(),tabChange:()=>this.onTabChange(),copy:()=>this.onCopy()},this.attachEvents()}defaults(){return{readOnly:!1,disabled:!1,eventHandler:null,hideOnTabChange:!1}}attachEvents(){this.input.addEventListener("keydown",this.events.input),this.toggle.addEventListener("click",this.events.toggle),this.config.get("hideOnTabChange")&&document.addEventListener("visibilitychange",this.events.tabChange),this.copy&&this.copy.addEventListener("click",this.events.copy)}destruct(){this.input.removeEventListener("keydown",this.events.input),this.toggle.removeEventListener("click",this.events.toggle),this.config.get("hideOnTabChange")&&document.removeEventListener("visibilitychange",this.events.tabChange),this.copy&&this.copy.removeEventListener("click",this.events.copy),this.input=null,this.toggle=null,this.icon=null,this.loader=null,super.destruct()}onInput(){this.clean&&(this.clean=!1,this.input.value="")}onToggle(){""!==this.input.value&&this.clean?this.reveal():this.toggleVisibility()}onTabChange(){document.hidden&&!this.hidden&&this.toggleVisibility()}onCopy(){new Promise((t,e)=>{""!==this.input.value&&this.clean?this.reveal(t).then(()=>t(),()=>e()):t()}).then(()=>{const t=this.hidden;this.hidden&&this.toggleVisibility(),this.input.focus(),this.input.select();try{const t=new Blob([this.input.value],{type:"text/plain"}),e=new ClipboardItem({"text/plain":t});navigator.clipboard.write([e])}catch(t){this.snowboard.error(`Clipboard API not supported - ${t}`)}this.input.blur(),t&&this.toggleVisibility()},t=>{this.snowboard.error(`Unable to retrieve hidden value - ${t}`)})}toggleVisibility(){this.input.setAttribute("type",this.hidden?"text":"password"),this.icon.classList.toggle("icon-eye"),this.icon.classList.toggle("icon-eye-slash"),this.hidden=!this.hidden}reveal(){return new Promise((t,e)=>{this.icon.style.visibility="hidden",this.loader.classList.remove("hide"),this.snowboard.request(this.input,this.config.get("eventHandler"),{success:e=>{this.input.value=e.value,this.clean=!1,this.icon.style.visibility="visible",this.loader.classList.add("hide"),this.toggleVisibility(),t()},error:t=>{e(new Error(t))}})})}}t.addPlugin("backend.formwidget.sensitive",e),t["backend.ui.widgethandler"]().register("sensitive","backend.formwidget.sensitive")})(window.Snowboard)},969:function(t,e,i){var n=i(935),s=i.n(n)()(function(t){return t[1]});s.push([t.id,"div[data-control=sensitive] a[data-copy],div[data-control=sensitive] a[data-toggle]{border:1px solid #d1d6d9;border-left:0;box-shadow:none}",""]),e.A=s}},function(t){t.O(0,[810],function(){return e=915,t(t.s=e);var e});t.O()}]);
|
||||
234
modules/backend/formwidgets/sensitive/assets/js/src/Sensitive.js
Normal file
234
modules/backend/formwidgets/sensitive/assets/js/src/Sensitive.js
Normal file
@@ -0,0 +1,234 @@
|
||||
import '../../less/sensitive.less';
|
||||
|
||||
((Snowboard) => {
|
||||
/**
|
||||
* Sensitive form widget.
|
||||
*
|
||||
* Renders a text field that acts as a password or secret field, but can be revealed by the
|
||||
* user.
|
||||
*
|
||||
* @author Ben Thomson <git@alfreido.com>
|
||||
* @copyright 2023 Winter CMS
|
||||
*/
|
||||
class Sensitive extends Snowboard.PluginBase {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
construct(element) {
|
||||
this.element = element;
|
||||
this.config = this.snowboard.dataConfig(this, element);
|
||||
this.clean = Boolean(element.dataset.clean);
|
||||
this.hidden = true;
|
||||
|
||||
// Child elements
|
||||
this.input = element.querySelector('[data-input]');
|
||||
this.toggle = element.querySelector('[data-toggle]');
|
||||
this.icon = element.querySelector('[data-icon]');
|
||||
this.loader = element.querySelector('[data-loader]');
|
||||
this.copy = element.querySelector('[data-copy]');
|
||||
|
||||
// Events
|
||||
this.events = {
|
||||
input: () => this.onInput(),
|
||||
toggle: () => this.onToggle(),
|
||||
tabChange: () => this.onTabChange(),
|
||||
copy: () => this.onCopy(),
|
||||
};
|
||||
|
||||
this.attachEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default options for this widget.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* - `data-read-only` - If set, this field will be read-only, but revealable.
|
||||
* - `data-disabled` - If set, this field will be disabled, and unrevealable.
|
||||
* - `data-event-handler=""` - Defines the AJAX event handler that will provide the revealed value.
|
||||
* - `data-hide-on-tab-change` - If set, this field will be hidden when the browser is switched
|
||||
* to another tab or minimized.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
defaults() {
|
||||
return {
|
||||
readOnly: false,
|
||||
disabled: false,
|
||||
eventHandler: null,
|
||||
hideOnTabChange: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches event listeners for several interactions.
|
||||
*/
|
||||
attachEvents() {
|
||||
this.input.addEventListener('keydown', this.events.input);
|
||||
this.toggle.addEventListener('click', this.events.toggle);
|
||||
|
||||
if (this.config.get('hideOnTabChange')) {
|
||||
// Watch for tab change or minimise
|
||||
document.addEventListener('visibilitychange', this.events.tabChange);
|
||||
}
|
||||
|
||||
if (this.copy) {
|
||||
this.copy.addEventListener('click', this.events.copy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
destruct() {
|
||||
this.input.removeEventListener('keydown', this.events.input);
|
||||
this.toggle.removeEventListener('click', this.events.toggle);
|
||||
|
||||
if (this.config.get('hideOnTabChange')) {
|
||||
// Watch for tab change or minimise
|
||||
document.removeEventListener('visibilitychange', this.events.tabChange);
|
||||
}
|
||||
|
||||
if (this.copy) {
|
||||
this.copy.removeEventListener('click', this.events.copy);
|
||||
}
|
||||
|
||||
this.input = null;
|
||||
this.toggle = null;
|
||||
this.icon = null;
|
||||
this.loader = null;
|
||||
|
||||
super.destruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input handler.
|
||||
*
|
||||
* Resets a clean field to empty if the user types anything in the field without revealing it.
|
||||
*/
|
||||
onInput() {
|
||||
if (this.clean) {
|
||||
this.clean = false;
|
||||
this.input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle handler.
|
||||
*
|
||||
* Reveals the value and toggles the visibility of a revealed value.
|
||||
*/
|
||||
onToggle() {
|
||||
if (this.input.value !== '' && this.clean) {
|
||||
this.reveal();
|
||||
} else {
|
||||
this.toggleVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab change handler.
|
||||
*
|
||||
* Fires when the browser is minimized or switched to another tab. This will hide the value.
|
||||
*/
|
||||
onTabChange() {
|
||||
if (document.hidden && !this.hidden) {
|
||||
this.toggleVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy handler.
|
||||
*
|
||||
* Copies the value to the clipboard.
|
||||
*/
|
||||
onCopy() {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
if (this.input.value !== '' && this.clean) {
|
||||
this.reveal(resolve).then(
|
||||
() => resolve(),
|
||||
() => reject(),
|
||||
);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
promise.then(
|
||||
() => {
|
||||
const isHidden = this.hidden;
|
||||
|
||||
if (this.hidden) {
|
||||
this.toggleVisibility();
|
||||
}
|
||||
|
||||
this.input.focus();
|
||||
this.input.select();
|
||||
|
||||
try {
|
||||
const blob = new Blob([this.input.value], { type: 'text/plain' });
|
||||
const item = new ClipboardItem({ 'text/plain': blob });
|
||||
navigator.clipboard.write([item]);
|
||||
} catch (error) {
|
||||
this.snowboard.error(`Clipboard API not supported - ${error}`);
|
||||
}
|
||||
|
||||
this.input.blur();
|
||||
if (isHidden) {
|
||||
this.toggleVisibility();
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.snowboard.error(`Unable to retrieve hidden value - ${error}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the value within the sensitive field.
|
||||
*/
|
||||
toggleVisibility() {
|
||||
this.input.setAttribute('type', (this.hidden) ? 'text' : 'password');
|
||||
this.icon.classList.toggle('icon-eye');
|
||||
this.icon.classList.toggle('icon-eye-slash');
|
||||
this.hidden = !this.hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reveals the value of the sensitive field.
|
||||
*
|
||||
* This will call an AJAX event handler to retrieve the value, allowing for values to be
|
||||
* manipulated or controlled by the server.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
reveal() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Show loader
|
||||
this.icon.style.visibility = 'hidden';
|
||||
this.loader.classList.remove('hide');
|
||||
|
||||
this.snowboard.request(this.input, this.config.get('eventHandler'), {
|
||||
success: (data) => {
|
||||
this.input.value = data.value;
|
||||
this.clean = false;
|
||||
|
||||
// Hide loader and reveal
|
||||
this.icon.style.visibility = 'visible';
|
||||
this.loader.classList.add('hide');
|
||||
this.toggleVisibility();
|
||||
resolve();
|
||||
},
|
||||
error: (error) => {
|
||||
reject(new Error(error));
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Snowboard.addPlugin('backend.formwidget.sensitive', Sensitive);
|
||||
Snowboard['backend.ui.widgethandler']().register('sensitive', 'backend.formwidget.sensitive');
|
||||
})(window.Snowboard);
|
||||
@@ -0,0 +1,10 @@
|
||||
@import "../../../../assets/less/core/boot.less";
|
||||
|
||||
div[data-control="sensitive"] {
|
||||
a[data-toggle],
|
||||
a[data-copy] {
|
||||
box-shadow: none;
|
||||
border: 1px solid @input-group-addon-border-color;
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user