Commit 0f2a10f0 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Merge branch 'F9526-RedesignRequired' into 'master'

F9526 redesign required

See merge request !206
parents 82ce3086 24971a75
Pipeline #2792 passed with stages
in 2 minutes and 40 seconds
......@@ -10,7 +10,7 @@
.. --------------------------------------------------
.. Best Practice T3 reST: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/CheatSheet.html
.. Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html
.. Italic *italic*are part of key or value
.. Italic *italic*
.. Bold **bold**
.. Code ``text``
.. External Links: `Bootstrap <http://getbootstrap.com/>`_
......@@ -1912,7 +1912,7 @@ Store: *VARS* - V
| slaveId | see `slaveId`_ |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| allRequiredGiven | Form save - Set during check of all required FE. If every *required* FE is given: *1*. Else: *0*. If there is no required FE: *1*. |
| | Even with 'formModeGlobal=skipRequiredCheck', the variable will be set. |
| | Even with `formModeGlobal` = `requiredOff` | `requiredOffButMark` the variable will be set. |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
.. _`store_vars_form_element_upload`:
......@@ -2484,7 +2484,7 @@ The CLIENT `submit_reason` shows the user action:
Example forwardPage
^^^^^^^^^^^^^^^^^^^
* `{{SELECT IF('{{formModeGlobal:S}}'='skipRequiredCheck', 'no', 'auto') }}`
* `{{SELECT IF('{{formModeGlobal:S}}'='requiredOff', 'no', 'auto') }}`
* `{{SELECT IF('{{submit_reason:CE:alnumx}}'='save', 'no', 'url'), '|http://example.com' }}`
Type: combined dynamic mode & URL/page
......@@ -2693,50 +2693,47 @@ The Form global mode `mode` is given by default with `{{formModeGlobal:SE:alnumx
Optional it might be defined via *Form.parameter* ::
mode=readonly|skipRequiredCheck
mode = readonly | requiredOff | requiredOffButMark
* `readonly`: all `FormElement` of the whole form are temporarily in `readonly` mode. This is a fast way to use an
existing *Form* just to display the form data, without a possibility for the user to change any data of the form.
* `skipRequiredCheck`:
* `requiredOff` | `requiredOffButMark` :
* Both modes are identical, but 'requiredOffButMark' will mark all missing fields to the user.
* All `FormElement` with `mode=required`, will be handled as `mode=show`.
* The user can save the form without providing all necessary data!
* The user will see complains about missing data - but again, the form data is saved!
* Check `STORE_VARS`_ for the variable `{{allRequiredGiven:V}}` - even without forcing the user to fill out all data,
this variable will show if the form has been filled completely.
* Check `STORE_VARS`_ for the variable `{{allRequiredGiven:V}}` - it shows if the form has been filled completely.
* Scenario 1:
* A form has one or more FormElement with 'fe.type=required'.
* Calling the form with `formModeGlobal=skipRequiredCheck` will allow the user to save the form, even if not all
* Calling the form with `formModeGlobal=requiredOff` will allow the user to save the form, even if not all
FE.type=required elements are given. Name this the 'draft' mode.
* Calling the form without `formModeGlobal=skipRequiredCheck` (this is the default), forces the user to fill out
* Calling the form without `formModeGlobal` (this is the default), forces the user to fill out
all FE.type=required fields. Name this the 'final submit' mode.
* Scenario 2:
* A form has one or more FormElement with 'fe.type=required'.
* Calling the form with `formModeGlobal=skipRequiredCheck` will allow the user to save the form, even if not all
* Calling the form with `formModeGlobal=requiredOff` will allow the user to save the form, even if not all
FE.type=required elements are given.
* Define an FE-Action 'afterSave', and do some action on `{{allRequiredGiven:V0}}` like::
{{UPDATE <table> SET dataValid={{allRequiredGiven:V0}} WHERE id={{id:R}} }}
* In your report, mark a button green or red, depending on the saved state.
* In your your application logic, you can open the next process step if all data is given by evaluating `{{allRequiredGiven:V0}}`.
* `requiredOff` is a deprecated synonym of `skipRequiredCheck`.
The following shows the same *Form* in the `regular`, `readonly` and `skipRequiredCheck` mode::
The following shows the same *Form* in the `regular`, `readonly`, `requiredOff` and `requiredOffButMark` mode::
10.sql = SELECT CONCAT('p:{{pageAlias:T}}&form=person&r=', p.id, '|Regular') as _pagee,
CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=readonly&r=', p.id, '|Readonly') as _pagee,
CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=skipRequiredCheck&r=', p.id, '|Required off') as _pagee
CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=requiredOff&r=', p.id, '|Required off') as _pagee
CONCAT('p:{{pageAlias:T}}&form=person&formModeGlobal=requiredOffButMark&r=', p.id, '|Required off') as _pagee
FROM Person AS p
FormElements
------------
......@@ -7602,8 +7599,8 @@ QFQ CSS Classes
* `qfq-100` - assigned to different tags, makes an element 'width: 100%'.
* `qfq-left`- assigned to different tags, Text align left.
* `qfq-sticky` - assigned to `<thead>`, makes the header sticky.
* `letter-no-break` - assigned to a `div` will protect a paragraph (CSS: page-break-before: avoid;) not to break and
page border, when converted to PDF via wkhtml. Take care that qfq-letter.css is included in TypoScript Setup.
* `letter-no-break` - assigned to a `div` will protect a paragraph (CSS: page-break-before: avoid;) not to break around
a page border (converted to PDF via wkhtml). Take care that `qfq-letter.css` is included in TypoScript setup.
Bootstrap
---------
......
......@@ -972,7 +972,7 @@ abstract class AbstractBuildForm {
*
* Accepted misbehaviour on forms with pills: if there is at least one editable element on the first pill,
* the other pills are not checked - independent if there was a definition on the first pill or not.
* Reason: checks happens per pill - if there is no explizit definition on the first pill, take the first
* Reason: checks happens per pill - if there is no explicit definition on the first pill, take the first
* editable element of that pill.
*/
private function checkAutoFocus() {
......@@ -987,7 +987,9 @@ abstract class AbstractBuildForm {
for ($i = 0; $i < count($this->feSpecNative); ++$i) {
// Only check native elements which will be shown
if ($this->feSpecNative[$i][FE_CLASS] == FE_CLASS_NATIVE &&
($this->feSpecNative[$i][FE_MODE] == FE_MODE_SHOW || $this->feSpecNative[$i][FE_MODE] == FE_MODE_REQUIRED)
($this->feSpecNative[$i][FE_MODE] == FE_MODE_SHOW
|| $this->feSpecNative[$i][FE_MODE] == FE_MODE_REQUIRED
|| $this->feSpecNative[$i][FE_MODE] == FE_MODE_SHOW_REQUIRED)
) {
// Check if there is an explicit definition.
if (isset($this->feSpecNative[$i][FE_AUTOFOCUS])) {
......@@ -1102,8 +1104,8 @@ abstract class AbstractBuildForm {
}
/**
* Create an array with standard elements for 'mode' (hidden, disabled, required) and add 'form-element',
* 'value'.
* Create an array with standard elements for 'mode' (hidden, disabled, required)
* and add 'form-element', 'value'.
* 'Generic Element Update': add via API_ELEMENT_UPDATE 'label' and 'note'.
* All collected data as array - will be later converted to JSON.
*
......@@ -1143,7 +1145,7 @@ abstract class AbstractBuildForm {
// }
}
if ($formElement[FE_MODE] == FE_MODE_REQUIRED) {
if ($formElement[FE_MODE] == FE_MODE_REQUIRED || $formElement[FE_MODE] == FE_MODE_SHOW_REQUIRED) {
$addClassRequired = HelperFormElement::getRequiredPositionClass($formElement[F_FE_REQUIRED_POSITION]);
}
......@@ -1249,6 +1251,7 @@ abstract class AbstractBuildForm {
switch ($feMode) {
case FE_MODE_SHOW:
case FE_MODE_SHOW_REQUIRED:
break;
case FE_MODE_REQUIRED:
$required = 'yes';
......@@ -1677,6 +1680,7 @@ abstract class AbstractBuildForm {
switch ($feMode) {
case FE_MODE_HIDDEN:
case FE_MODE_SHOW:
case FE_MODE_SHOW_REQUIRED:
break;
case FE_MODE_REQUIRED:
case FE_MODE_READONLY:
......@@ -2072,8 +2076,14 @@ abstract class AbstractBuildForm {
}
$labelAttribute = Support::doAttribute('title', $formElement[FE_TOOLTIP]);
$html = Support::wrapTag("<label $labelAttribute>", $html, true);
$html = Support::wrapTag("<div class='checkbox'>", $html, true);
$class = 'checkbox';
if ($formElement[FE_MODE] == FE_MODE_READONLY) {
$class .= ' qfq-disabled'; // necessary for own style checkboxes to display them 'disabled'
}
$html = Support::wrapTag("<label class='$class' $labelAttribute>", $html, true);
// $html = Support::wrapTag("<div class='checkbox'>", $html, true);
$json = $this->getFormElementForJson($htmlFormElementName, $valueJson, $formElement);
......@@ -2243,6 +2253,10 @@ abstract class AbstractBuildForm {
$orientation = ($formElement[FE_MAX_LENGTH] > 1) ? ALIGN_HORIZONTAL : ALIGN_VERTICAL;
$checkboxClass = ($orientation === ALIGN_HORIZONTAL) ? 'checkbox-inline' : 'checkbox';
if ($formElement[FE_MODE] == FE_MODE_READONLY) {
$checkboxClass .= ' qfq-disabled'; // necessary for own style checkboxes to display them 'disabled'
}
$br = '';
$flagFirst = true;
......@@ -2279,7 +2293,7 @@ abstract class AbstractBuildForm {
$htmlElement = Support::wrapTag('<label>', $htmlElement);
}
$htmlElement = Support::wrapTag("<div class='$checkboxClass'>", $htmlElement, true);
$htmlElement = Support::wrapTag("<label class='$checkboxClass'>", $htmlElement, true);
// control orientation
if ($formElement[FE_MAX_LENGTH] > 1) {
......@@ -2498,6 +2512,9 @@ abstract class AbstractBuildForm {
$orientation = ($formElement[FE_MAX_LENGTH] > 1) ? ALIGN_HORIZONTAL : ALIGN_VERTICAL;
$radioClass = ($orientation === ALIGN_HORIZONTAL) ? 'radio-inline' : 'radio';
if ($formElement[FE_MODE] == FE_MODE_READONLY) {
$radioClass .= ' qfq-disabled';
}
$radioOuterTag = ($orientation === ALIGN_HORIZONTAL) ? 'label' : 'div';
$br = '';
......
......@@ -599,9 +599,10 @@ class BuildFormBootstrap extends AbstractBuildForm {
$formModeGlobal = $this->formSpec[F_MODE_GLOBAL] ?? '';
}
if ($formModeGlobal == F_MODE_SKIP_REQUIRED_CHECK || $formModeGlobal == F_MODE_REQUIRED_OFF) {
$attribute[DATA_SKIP_REQUIRED_CHECK] = 'true';
if ($formModeGlobal == F_MODE_REQUIRED_OFF_BUT_MARK || $formModeGlobal == F_MODE_REQUIRED_OFF) {
$attribute[DATA_REQUIRED_OFF_BUT_MARK] = 'true';
}
if (isset($this->formSpec[F_SAVE_BUTTON_ACTIVE]) && $this->formSpec[F_SAVE_BUTTON_ACTIVE] != '0') {
$attribute[DATA_ENABLE_SAVE_BUTTON] = 'true';
}
......@@ -803,7 +804,7 @@ EOF;
}
}
if ($formElement[FE_MODE] == FE_MODE_REQUIRED) {
if ($formElement[FE_MODE] == FE_MODE_REQUIRED || $formElement[FE_MODE] == FE_MODE_SHOW_REQUIRED) {
$addClassRequired = HelperFormElement::getRequiredPositionClass($formElement[F_FE_REQUIRED_POSITION]);
}
......
......@@ -836,7 +836,7 @@ const API_ANSWER_REDIRECT_URL_SKIP_HISTORY = 'url-skip-history';
const API_TYPEAHEAD_KEY = 'key';
const API_TYPEAHEAD_VALUE = 'value';
const DATA_SKIP_REQUIRED_CHECK = 'data-skip-required-check';
const DATA_REQUIRED_OFF_BUT_MARK = 'data-required-off-but-mark';
const DATA_HIDDEN = 'data-hidden';
const DATA_DISABLED = 'data-disabled';
......@@ -1010,8 +1010,9 @@ const F_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN = 'typeAheadLdapSearchPerToken';
const F_MODE = 'mode';
const F_MODE_READONLY = 'readonly';
const F_MODE_REQUIRED_OFF = 'requiredOff'; // deprecated, #9617
const F_MODE_SKIP_REQUIRED_CHECK = 'skipRequiredCheck';
const F_MODE_REQUIRED_OFF = 'requiredOff';
const F_MODE_REQUIRED_OFF_BUT_MARK = 'requiredOffButMark';
const F_MODE_SKIP_REQUIRED_CHECK = 'skipRequiredCheck'; // deprecated since third revision of #9617
const F_MODE_GLOBAL = 'formModeGlobal';
const F_SAVE_BUTTON_ACTIVE = 'saveButtonActive';
......@@ -1063,6 +1064,7 @@ const CLIENT_REST_FORM = '_form';
const FE_MODE_SHOW = 'show';
const FE_MODE_READONLY = 'readonly';
const FE_MODE_REQUIRED = 'required';
const FE_MODE_SHOW_REQUIRED = 'showRequired';
const FE_MODE_HIDDEN = 'hidden';
const FE_CLASS_NATIVE = 'native';
......
......@@ -408,7 +408,9 @@ EOF;
EOF;
}
$skip = (!($formElement[FE_MODE] == FE_MODE_SHOW || $formElement[FE_MODE] == FE_MODE_REQUIRED));
$skip = (!($formElement[FE_MODE] == FE_MODE_SHOW
|| $formElement[FE_MODE] == FE_MODE_REQUIRED
|| $formElement[FE_MODE] == FE_MODE_SHOW_REQUIRED));
// LOCK
if (!$skip && HelperFormElement::booleParameter($formElement[FE_INPUT_EXTRA_BUTTON_LOCK] ?? '-')) {
......
......@@ -1209,6 +1209,8 @@ class Support {
case FE_MODE_REQUIRED:
if ($formMode == F_MODE_READONLY) {
$feMode = FE_MODE_READONLY;
} elseif ($formMode == F_MODE_REQUIRED_OFF && $feMode == FE_MODE_REQUIRED) {
$feMode = FE_MODE_SHOW_REQUIRED;
}
break;
......
......@@ -224,7 +224,7 @@ class QuickFormQuery {
}
/**
* Main entrypoint for display content: a) form and/or b) report
* Main entry point to display content: a) form and/or b) report
*
* @return string
* @throws \CodeException
......
......@@ -549,7 +549,13 @@ class Save {
$formModeGlobal = $this->formSpec[F_MODE_GLOBAL] ?? '';
}
$reportRequiredFailed = ($formModeGlobal != F_MODE_REQUIRED_OFF && $formModeGlobal != F_MODE_SKIP_REQUIRED_CHECK);
$reportRequiredFailed = true;
switch ($formModeGlobal) {
case F_MODE_REQUIRED_OFF:
case F_MODE_REQUIRED_OFF_BUT_MARK:
$reportRequiredFailed = false;
break;
}
$clientValues = $this->store::getStore(STORE_FORM);
......
......@@ -326,6 +326,8 @@ class Config {
SYSTEM_CMD_WKHTMLTOPDF => '/opt/wkhtmltox/bin/wkhtmltopdf',
F_CLASS => 'qfq-notify',
F_CLASS_PILL => 'qfq-color-grey-1',
F_CLASS_BODY => 'qfq-color-grey-2',
......
......@@ -184,7 +184,7 @@ class FillStoreForm {
if ($formMode == FORM_UPDATE && $formModeGlobal == '') {
# During 'update': fake all elements to be not 'required'.
$formModeGlobal = F_MODE_SKIP_REQUIRED_CHECK;
$formModeGlobal = F_MODE_REQUIRED_OFF;
}
// If called through 'api/...': get STORE_TYPO3 via SIP parameter.
......@@ -289,6 +289,7 @@ class FillStoreForm {
if ($formElement[FE_DYNAMIC_UPDATE] === 'yes' ||
$formElement[FE_MODE] === FE_MODE_REQUIRED ||
$formElement[FE_MODE] === FE_MODE_SHOW_REQUIRED ||
$formElement[FE_MODE] === FE_MODE_SHOW ||
(isset($formElement[FE_PROCESS_READ_ONLY]) && $formElement[FE_PROCESS_READ_ONLY] != '0')) {
......
......@@ -409,6 +409,8 @@ class StoreTest extends TestCase {
F_FE_DATA_PATTERN_ERROR_SYSTEM => F_FE_DATA_PATTERN_ERROR_DEFAULT,
SYSTEM_SECURITY_FAILED_AUTH_DELAY => '3',
SYSTEM_SQL_LOG_MODE_AUTOCRON => 'error',
F_CLASS => 'qfq-notify',
];
$body = <<< EOT
......
......@@ -155,7 +155,7 @@ labelAlign = left
# cat=form-layout/layout; type=string; label=CSS class QFQ container:Default is empty. Empty, if the page content is already wrapped in a Bootstrap container. Else 'container'.
cssClassQfqContainer =
# cat=form-layout/layout; type=string; label=CSS class QFQ form:Default is empty. If given wrap around QFQ-Form. E.g. 'qfq-color-base'.
# cat=form-layout/layout; type=string; label=CSS class QFQ form:Default is 'qfq-notify''. If given wrap around QFQ-Form. E.g. 'qfq-color-base'.
cssClassQfqForm =
# cat=form-layout/layout; type=string; label=CSS class of pills on the HTML form:Default is 'qfq-color-grey-1'. Wrap around title bar for pills: CSS Class, typically a background color.
......
......@@ -67,7 +67,7 @@ var QfqNS = QfqNS || {};
this.form = new n.Form(this.formId, false);
}
if ($('#' + QfqNS.escapeJqueryIdSelector(this.formId)).data('skip-required-check')) {
if ($('#' + QfqNS.escapeJqueryIdSelector(this.formId)).data('required-off-but-mark')) {
this.skipRequiredCheck = true;
} else {
this.skipRequiredCheck = false;
......@@ -152,6 +152,11 @@ var QfqNS = QfqNS || {};
this.form.on('form.validation.failed', this.validationError);
this.form.on('form.validation.success', this.validationSuccess);
$(".radio-inline").append($("<span>", { class: "checkmark", aria: "hidden"}));
$(".checkbox-inline").append($("<span>", { class: "checkmark", aria: "hidden"}));
$(".radio").append($("<span>", { class: "checkmark", aria: "hidden"}));
$(".checkbox").append($("<span>", { class: "checkmark", aria: "hidden"}));
};
n.QfqForm.prototype.on = n.EventEmitter.onMixin;
......@@ -523,7 +528,7 @@ var QfqNS = QfqNS || {};
if (data.status === "error") {
this._createError("Error while updating form:<br>" +
(data.message ? data.message : "No reason given"));
(data.message ? data.message : "No reason given"));
return;
}
......@@ -619,17 +624,39 @@ var QfqNS = QfqNS || {};
if (tabId) {
this.bsTabs.activateTab(tabId);
}
var form = document.getElementById(this.form.formId);
var inputs = form.elements;
for (var i = 0; i < inputs.length; i++) {
var e = inputs[i];
if(!e.willValidate) {
continue;
}
if(!e.checkValidity()) {
var updateTabId = this.bsTabs.getContainingTabIdForFormControl(e.getAttribute('name'));
if(updateTabId != tabId) {
this.bsTabs.addDot(updateTabId);
}
}
}
}
// Since we might have switched the tab, re-validate to highlight errors
this.form.$form.validator('update');
this.form.$form.validator('validate');
alert = new n.Alert("Form is incomplete.", "warning");
alert.timeout = 3000;
alert.show();
this.form.$form.each(function() {
if (!$(this).validate) {
}
});
if (!this.skipRequiredCheck) {
alert = new n.Alert("Form is incomplete.", "warning");
alert.timeout = 3000;
alert.show();
return;
}
}
......@@ -640,7 +667,6 @@ var QfqNS = QfqNS || {};
this.clearAllValidationStates();
}
submitReason = {
"submit_reason": this.lastButtonPress === "close" ? "save,close" : this.lastButtonPress
};
......@@ -1100,6 +1126,7 @@ var QfqNS = QfqNS || {};
};
n.QfqForm.prototype.getFormGroupByControlName = function (formControlName) {
console.log("Form Control Name: " + formControlName);
var $formControl = $("[name='" + formControlName + "']");
if ($formControl.length === 0) {
n.Log.debug("QfqForm.setValidationState(): unable to find form control with name '" + formControlName + "'");
......@@ -1123,12 +1150,13 @@ var QfqNS = QfqNS || {};
var $formGroup = this.getFormGroupByControlName(formControlName);
if ($formGroup) {
$formGroup.addClass("has-" + state);
$formGroup.addClass("testitest");
}
};
n.QfqForm.prototype.resetValidationState = function (formControlName) {
var $formGroup = this.getFormGroupByControlName(formControlName);
$formGroup.removeClass("has-warning");
var $formGroup = this.getFormGroupByControlName(formControlName).find('input');
$formGroup.removeClass("has-danger");
$formGroup.removeClass("has-error");
$formGroup.removeClass("has-success");
$formGroup.removeClass("has-danger");
......@@ -1177,6 +1205,7 @@ var QfqNS = QfqNS || {};
.addClass("help-block")
.attr("data-qfq", "validation-message")
.append(text)
.prepend($("<div>", { class: "arrow arrow-up"}))
);
};
......@@ -1321,4 +1350,4 @@ var QfqNS = QfqNS || {};
window.history.back();
};
})(QfqNS);
})(QfqNS);
\ No newline at end of file
......@@ -36,9 +36,9 @@ i.@{spinner_class} {
.qfq-dot {
display: inline-block;
height: 5px;
width: 5px;
margin: 2px;
height: 9px;
width: 9px;
margin: 1px;
margin-left: 5px;
background-color: #f2b867;
border-radius: 50%;
......@@ -48,6 +48,248 @@ i.@{spinner_class} {
margin-top: 10px;
}
/* CUSTOM RADIOS */
/* Hide the browser's default radio */
.radio-inline>input[type=radio], .radio>input[type=radio] {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Customize the label (the container) */
.radio-inline, .radio {
display: inline-block;
position: relative;
padding-left: 1.8em;
padding-top: 0 !important;
margin-top: 7px !important;
margin-right: 10px;
cursor: pointer;
font-size: 1em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.radio {
display: block;
}
/* Create a custom radio button */
.radio-inline .checkmark, .radio .checkmark {
position: absolute;
top: 0;
left: 0;
height: 18px;
width: 18px;
background-color: #fff;
border-radius: 50%;
border: 2px solid #ccc;
}
.radio-inline:hover input ~ .checkmark, .radio:hover input ~ .checkmark {
border-color: #2196F3;
}
.radio-inline input[type=radio]:checked ~ .checkmark, .radio input[type=radio]:checked ~ .checkmark {
background-color: #2196F3;
border-color: #2196F3;
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the indicator (dot/circle) when checked */
.radio-inline input[type=radio]:checked ~ .checkmark:after, .radio input[type=radio]:checked ~ .checkmark:after {
display: block;
}
/* Style the indicator (dot/circle) */
.radio-inline .checkmark:after, .radio .checkmark:after {
top: 3px;
left: 3px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #fff;
}
/* CUSTOM CHECKBOXES */
/* Hide the browser's default radio */
.checkbox-inline>input[type=checkbox] {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Customize the label (the container) */
.checkbox-inline, .checkbox {
display: inline-block;
position: relative;
padding-left: 1.8em;
padding-top: 0 !important;
margin-top: 7px !important;
margin-right: 10px;
cursor: pointer;
font-size: 1em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.checkbox-inline input:read-only ~ .checkmark {
cursor: not-allowed;
pointer-events: none;
}
.checkbox {
display: block;
}
.qfq-disabled {
cursor: not-allowed !important;
pointer-events: none !important;
}
/* Create a custom radio button */
.checkbox-inline .checkmark, .checkbox .checkmark {
position: absolute;
top: 0;
left: 0;
height: 18px;
width: 18px;
background-color: #fff;
border-radius: 4px;
border: 2px solid #ccc;
}
.checkbox-inline:hover input ~ .checkmark, .checkbox:hover input ~ .checkmark {
border-color: #66afe9;
}
.checkbox-inline input:checked ~ .checkmark, .checkbox input:checked ~ .checkmark {
background-color: #2196F3;
border-color: #2196F3;
}
.checkbox-inline.qfq-disabled input ~ .checkmark, .checkbox.qfq-disabled input ~ .checkmark,
.checkbox-inline.qfq-disabled:hover input ~ .checkmark, .checkbox.qfq-disabled:hover input ~ .checkmark,
.radio-inline.qfq-disabled:hover input ~ .checkmark, .radio.qfq-disabled:hover input ~ .checkmark,