public function register_scripts() {
- // Base forms functionality
- wp_register_script('cube-forms', asset('scripts/forms.js'), ['jquery'], null, true);
- wp_enqueue_script('cube-forms'); // Always needed, so enqueue it directly here
+ //=== Cube Forms
+ // Base forms functionality - enqueued directly since it is always required
+ wp_enqueue_script('cube-forms', asset('scripts/forms.js'), ['jquery'], null, true);
// JS variables
wp_add_inline_script(
'before' // Output before script so we can use the vars
);
+
+ //=== Parsley JS Validation
+ wp_enqueue_script('parsleyjs', asset('scripts/parsley/parsley.min.js'), ['jquery'], null, true);
+
+ // Load localisation if available (English is default so not needed)
+ $parsley_locales = ['ar', 'fr', 'ru']; // List of possible locales (see webpack.mix.js for which ones are copied)
+ if (function_exists('pll_current_language')) {
+ $current_locale = pll_current_language('slug');
+
+ if (in_array($current_locale, $parsley_locales)) {
+ // Include it inline since it's small and to save an extra HTTP request
+ wp_add_inline_script('parsleyjs', asset("scripts/parsley/locale/{$current_locale}.js")->contents());
+ }
+ }
+
+ // Parsley JS initialisation and overrides
+ // Ref: https://stackoverflow.com/a/30122442
+ wp_add_inline_script('parsleyjs', "
+ const parsleyConfig = {
+ classHandler: function(parsleyField) {
+ var fieldWrapper = parsleyField.\$element.parents('.form-field');
+ return (fieldWrapper.length > 0) ? fieldWrapper : parsleyField;
+ },
+
+ errorsContainer: function(parsleyField) {
+ var inputWrapper = parsleyField.\$element.parents('.form-field-input');
+ return (inputWrapper.length > 0) ? inputWrapper : parsleyField;
+ }
+ };
+
+ jQuery('.cube-form').parsley(parsleyConfig);
+
+ // On validation errors, scroll to the first invalid input
+ jQuery.listen('parsley:field:error', function() {
+ window.scrollTo({...jQuery('.parsley-error').first().offset(), ...{behavior: 'smooth'}});
+ });
+
+ ");
+
+
+ //=== Flatpickr Calendar
+
// Elementor has an older, incompatible version of Flatpickr so we need to replace it with our version
if (wp_script_is('flatpickr', 'registered')) {
wp_deregister_script('flatpickr');
}
}
- $message = view('forms.common.email', compact('data'));
+ $message = view('forms.common.email', compact('data', 'subject'));
$success = wp_mail($to, $subject, $message, $headers);
'placeholder' => $field['title'],
'field_before' => '',
'field_after' => '',
+ 'validation' => '',
];
$settings = array_merge($default_settings, $settings);
+ // Set up default validation that can be overridden by setting validation field
+ if ($field['required'] && empty($settings['validation'])) {
+ $settings['validation'] = 'required'; // Default but could also be set with other ParsleyJS attributes
+ }
+
$res = '';
if ($settings['show_title']) {
foreach ($options as $option) {
$res .= '<label title="'. $option .'">';
- $res .= '<input type="'. $type .'" name="'. $input_name .'" value="'. $option .'">';
+ $res .= '<input type="'. $type .'" name="'. $input_name .'" value="'. $option .'" '. $settings['validation'] .'>';
if (isset($settings['show_labels']) && $settings['show_labels']) {
$res .= '<span class="form-label">'. $option .'</span>';
wp_enqueue_script('cube-flatpickr');
$res = '<div data-flatpickr="'. esc_attr(json_encode($flatpickr)) .'" class="flex">';
- $res .= '<input type="date" placeholder="'. $settings['placeholder'] .'" name="'. $name .'" data-input>';
+ $res .= '<input type="date" placeholder="'. $settings['placeholder'] .'" name="'. $name .'" data-input '. $settings['validation'] .'>';
if ($settings['show_icon']) {
$res .= '<img src="' . asset('images/calendar.svg') . '" class="ml-2 mr-6 cursor-pointer" data-toggle>';
* @return string
*/
public function textarea($name, $settings = []) {
- return '<textarea name="'. $name .'"></textarea>';
+ return '<textarea name="'. $name .'" '. $settings['validation'] .'></textarea>';
}
/**
* @return string
*/
public function text($name, $settings = []) {
- return '<input type="text" name="'. $name .'" placeholder="'. $settings['placeholder'] .'">';
+ return '<input type="text" name="'. $name .'" placeholder="'. $settings['placeholder'] .'" '. $settings['validation'] .'>';
}
/**
* @return string
*/
public function email($name, $settings = []) {
- return '<input type="email" name="'. $name .'" placeholder="'. $settings['placeholder'] .'">';
+ return '<input type="email" name="'. $name .'" placeholder="'. $settings['placeholder'] .'" '. $settings['validation'] .'>';
}
/**
$options = $settings['placeholder'] + $options; // Prepend to options
}
- $res = '<select name="'. $name .'">';
+ $res = '<select name="'. $name .'" '. $settings['validation'] .'>';
foreach ($options as $value => $label) {
$res .= '<option value="'. $value .'">'. $label .'</option>';
}
* @param $title
* @param $type
* @param array $options
+ * @param bool $required
*/
- public function add_field($name, $title, $type, $options = []) {
+ public function add_field($name, $title, $type, $options = [], $required = true) {
$this->fields[$name]['title'] = $title;
$this->fields[$name]['type'] = $type;
+ $this->fields[$name]['required'] = $required;
if (!empty($options)) {
$this->set_field_options($name, $options);
$this->add_field('date-first-symptoms', __('Date des premiers symptômes :', 'ccv'), self::DATE);
- $this->add_field('date-pain-since', __('Douleurs permanentes depuis (le cas échéant) :', 'ccv'), self::DATE);
+ $this->add_field('date-pain-since', __('Douleurs permanentes depuis (le cas échéant) :', 'ccv'), self::DATE, [], false);
$this->add_field('pain-arms-legs', __('Avez-vous des douleurs dans les bras ou les jambes (sciatiques, cruralgies, névralgies) ?', 'ccv'), self::BINARY);
__('Haut de la jambe droite', 'ccv'),
__('Bas de la jambe gauche', 'ccv'),
__('Bas de la jambe droite', 'ccv'),
- ]
+ ],
+ false // Set not required
);
$this->add_field('main-pain', __('La douleur principale est-elle ?', 'ccv'), self::RADIO,
$this->add_field('tingling-numbness', __('Avez-vous des fourmillements ou une sensation d’engourdissement dans un des membres ? ', 'ccv'), self::BINARY);
- $this->add_field('tingling-numbness-date', __('Si oui depuis quand ?', 'ccv'), self::DATE);
+ $this->add_field('tingling-numbness-date', __('Si oui depuis quand ?', 'ccv'), self::DATE, [], false);
$this->add_field('strength-loss', __('Avez-vous une perte de force importante dans un des membres ?', 'ccv'), self::BINARY);
- $this->add_field('strength-loss-date', __('Si oui depuis quand ?', 'ccv'), self::DATE);
+ $this->add_field('strength-loss-date', __('Si oui depuis quand ?', 'ccv'), self::DATE, [], false);
//=== TREATMENTS
- $this->add_field('medication', __('Indiquez ici les médicaments que vous avez pris pour vos douleurs (le cas échéant)', 'ccv'), self::TEXTAREA);
+ $this->add_field('medication', __('Indiquez ici les médicaments que vous avez pris pour vos douleurs (le cas échéant)', 'ccv'), self::TEXTAREA, [], false);
$this->add_field('kine-osteo', __('Kinésithérapie ou ostéopathie', 'ccv'), self::BINARY);
$this->add_field('corset', __('Corset ou ceinture lombaire', 'ccv'), self::BINARY);
$this->add_field('hospitalisation', __('Séjour en hospitalisation', 'ccv'), self::BINARY);
$this->add_field('infiltration', __('Infiltration ou thermocoagulation', 'ccv'), self::BINARY);
- $this->add_field('surgeries', __('Indiquez ici vos précédentes chirurgies de la colonne et leurs dates (le cas échéant)', 'ccv'), self::TEXTAREA);
+ $this->add_field('surgeries', __('Indiquez ici vos précédentes chirurgies de la colonne et leurs dates (le cas échéant)', 'ccv'), self::TEXTAREA, [], false);
//=== IMAGERY
- $this->add_field('imagery-type', __('Imagerie', 'ccv'), null); // This is a special case and will be output manually so only using this for the e-mail label
- $this->add_field('imagery-online', __('Images en ligne', 'ccv'), self::TEXTAREA); // Again, a manually handled field
- $this->add_field('imagery-posted', __('Images envoyé par courrier', 'ccv'), self::CHECKBOX);
+ $this->add_field('imagery-type', __('Imagerie', 'ccv'), null, [], false); // This is a special case and will be output manually so only using this for the e-mail label
+ $this->add_field('imagery-online', __('Images en ligne', 'ccv'), self::TEXTAREA, [], false); // Again, a manually handled field
+ $this->add_field('imagery-posted', __('Images envoyé par courrier', 'ccv'), self::CHECKBOX, [], false);
//=== PERSONAL INFORMATION
$this->add_field('last-name', _x('Nom', 'Nom de famille', 'ccv'), self::TEXT);
$this->add_field('email', __('Email', 'ccv'), self::EMAIL);
$this->add_field('sex', __('Sexe', 'ccv'), self::RADIO, [_x('M', 'Sexe (M)', 'ccv'), _x('F', 'Sexe (F)', 'ccv')]);
$this->add_field('age', __('Âge :', 'ccv'), self::TEXT);
- $this->add_field('message', __('Avez vous un message (ou une demande) spécifique à nous formuler ?', 'ccv'), self::TEXTAREA);
+ $this->add_field('message', __('Avez vous un message (ou une demande) spécifique à nous formuler ?', 'ccv'), self::TEXTAREA, [], false);
// Special field: if a surgeon is selected, their e-mail address will be override the default delivery address
$this->add_field('surgeon', __('Chirurgien spécifique'), self::SELECT,
'Dr Grégory EDGARD-ROSA' => 'xxxxx@ccv-montpellier.fr',
'Dr Martin GRAU ORTIZ' => 'xxxxx@ccv-montpellier.fr',
'Dr Caroline HIRSH' => 'xxxxx@ccv-montpellier.fr',
- ]);
+ ],
+ false // Set not required
+ );
}
public function pre_process() {
"mix-tailwindcss": "^1.0.2",
"mmenu-light": "^2.3.1",
"npm-run-all": "^4.1",
+ "parsleyjs": "^2.9.2",
"purgecss-with-wordpress": "^1.1.0",
"rimraf": "^2.6",
"rupture": "^0.7.1",
margin-bottom: 0.75em
.form-field-input
- > *:not(:last-child)
- margin-right: 1.5em
+ > *:not(:first-child)
+ margin-left: 1.5em
label
white-space: nowrap // Ensure text stays beside input control
// ParsleyJS validation
.parsley-errors-list
- @apply text-pink
- position: relative
- top: -2.45em
+ @apply text-red
list-style: none
+ position: absolute
+ left: 0
+ bottom: -2em
padding: 0
+ margin-left: 0 !important
font-size: 0.7em
- text-align: right
+ white-space: nowrap
+
+ li:before
+ display: none
.parsley-error
- border-color: theme('colors.red') !important
+ padding-bottom: 0.5em // Add a little space for the validation message
+
+ .form-field-input
+ position: relative
+
+ input, textarea, select, .form-label:before
+ border-color: theme('colors.red') !important
/** Search form */
body {
font-family: Arial, sans-serif;
}
+ table {
+ border-style: solid;
+ border-color: #aaa;
+ border-width: 1px 1px 0 1px;
+ }
+ td {
+ padding: 12px 18px 12px 12px;
+ border-bottom: 1px solid #aaa;
+ }
</style>
</head>
<body>
- <table cellpadding="10" cellspacing="0" border="0">
+
+ <h1 style="font-size: 24px; margin-bottom: 1.5em;">{{ $subject }}</h1>
+
+ <table cellpadding="0" cellspacing="0" border="0">
@foreach($data as $label => $value)
- <tr @if ($loop->even) bgcolor="#eeeeee" @endif>
- <td valign="top"><strong>{{ $label }}</strong></td>
+ <tr @if($loop->even) bgcolor="#eeeeee" @endif>
+ <td valign="top" align="right"><strong>{{ rtrim($label, ':') }}</strong></td>
<td>
@if (is_array($value))
- <ul style="margin-left: 0">
+ <ul style="margin: 0; padding-left: 1em;">
<li>{!! implode('</li><li>', $value) !!}</li>
</ul>
@else
.js(src`scripts/customizer.js`, 'scripts')
.extract(['mmenu-light']); // Extract any libraries that will rarely change to a vendor.js file
+// ParsleyJS validator
+mix.copy('node_modules/parsleyjs/dist/parsley.min.js', publicPath`scripts/parsley`);
+// Set which locales to copy
+['ar', 'fr', 'ru']
+ .forEach(locale => mix.copy(`node_modules/parsleyjs/dist/i18n/${locale}.js`, publicPath`scripts/parsley/locale`));
+
// Lity lightbox
mix.copy('node_modules/lity/dist/lity.js', publicPath`scripts`);
mix.stylus(src`styles/components/lity-lightbox.styl`, 'styles/lity.css', stylusConfig).options(stylusOptions).tailwind();
merge-stream "^2.0.0"
supports-color "^7.0.0"
+jquery@>=1.8.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.0.tgz#9980b97d9e4194611c36530e7dc46a58d7340fc9"
+ integrity sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ==
+
js-dom-router@^1.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/js-dom-router/-/js-dom-router-1.0.0.tgz#c4b05674f6f0734f5bf9b9f389f1b3d9c62e4103"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+parsleyjs@^2.9.2:
+ version "2.9.2"
+ resolved "https://registry.yarnpkg.com/parsleyjs/-/parsleyjs-2.9.2.tgz#67c96961d371821f2623965fa2cc81a4522874cb"
+ integrity sha512-DKS2XXTjEUZ1BJWUzgXAr+550kFBZrom2WYweubqdV7WzdNC1hjOajZDfeBPoAZMkXumJPlB3v37IKatbiW8zQ==
+ dependencies:
+ jquery ">=1.8.0"
+
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"