class ScssNumber in SCSS Compiler 1.0.x
A form element to represent Sass numbers with a unit.
Copyright (C) 2021 Library Solutions, LLC (et al.).
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
Plugin annotation
@FormElement("compiler_scss_number");
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\compiler_scss\Element\ScssNumber
- class \Drupal\Core\Render\Element\FormElement implements FormElementInterface
- class \Drupal\Core\Render\Element\RenderElement implements ElementInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of ScssNumber
File
- src/
Element/ ScssNumber.php, line 22
Namespace
Drupal\compiler_scss\ElementView source
class ScssNumber extends FormElement {
/**
* Used when filtering the allowed units list to include "absolute" units.
*
* Below is a list of included units:
*
* - cm: centimeters
* - in: inches
* - mm: millimeters
* - pc: picas
* - pt: points
* - px: pixels
*
* @var int
*/
const UNIT_ABSOLUTE = 0b1;
/**
* Used when filtering the allowed units list to include "angle" units.
*
* Below is a list of included units:
*
* - deg: degrees
* - grad: gradians
* - rad: radians
* - turn: turns
*
* @var int
*/
const UNIT_ANGLE = 0b10;
/**
* Used when filtering the allowed units list to include "angle" units.
*
* Below is a list of included units:
*
* - Hz: hertz
* - kHz: kilohertz
*
* @var int
*/
const UNIT_FREQUENCY = 0b100;
/**
* Used when filtering the allowed units list to include the percent unit.
*/
const UNIT_PERCENT = 0b1000;
/**
* Used when filtering the allowed units list to include "relative" units.
*
* Below is a list of included units:
*
* - rem: × the root font size
* - em: × the element font size
* - lh: × the line height of the element
* - ex: × the x-height of the element font
* - ch: × the width of "0" in the element font
* - vh: × 1% of the viewport height
* - vw: × 1% of the viewport width
* - vmax: × 1% of the larger viewport dimension
* - vmin: × 1% of the smaller viewport dimension
*
* @var int
*/
const UNIT_RELATIVE = 0b10000;
/**
* Used when filtering the allowed units list to include "resolution" units.
*
* Below is a list of included units:
*
* - dpcm: dots per centimeter
* - dpi: dots per inch
* - dppx: dots per pixel
*
* @var int
*/
const UNIT_RESOLUTION = 0b100000;
/**
* Used when filtering the allowed units list to include "time" units.
*
* Below is a list of included units:
*
* - ms: milliseconds
* - s: seconds
*
* @var int
*/
const UNIT_TIME = 0b1000000;
/**
* Used when filtering the allowed units list to include all supported units.
*
* @var int
*/
const UNIT_ALL = self::UNIT_ABSOLUTE | self::UNIT_ANGLE | self::UNIT_FREQUENCY | self::UNIT_PERCENT | self::UNIT_RELATIVE | self::UNIT_RESOLUTION | self::UNIT_TIME;
/**
* Used when filtering the allowed units list to include absolute & relative.
*
* @var int
*/
const UNIT_LENGTH = self::UNIT_ABSOLUTE | self::UNIT_RELATIVE;
/**
* Used when filtering the allowed units list to include length & percent.
*
* @var int
*/
const UNIT_LENGTH_PERCENT = self::UNIT_LENGTH | self::UNIT_PERCENT;
/**
* An array of unit groups keyed by their corresponding filter constant.
*
* Each group is an array of unit labels keyed by unit name.
*
* @var \Drupal\Core\StringTranslation\TranslatableMarkup[string][int]
*/
protected static $units = [];
/**
* Get an associative array of allowed unit options, keyed by unit name.
*
* @param int|null $type
* The type(s) of unit to retrieve. See the contsants in this class for all
* possible unit types. If null, all types are included (default: NULL).
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup[string]
* An associative array of allowed unit options, keyed by unit name.
*/
public static function getAllowedUnits($type = NULL) {
$groups = array_keys(self::$units);
$type = is_int($type) ? $type : self::UNIT_ALL;
if (empty(self::$units)) {
self::initializeUnits();
}
$result = array_keys(self::$units);
$result = array_reduce($result, function ($units, $group) use ($type) {
return $units + (($type & $group) > 0 ? self::$units[$group] : []);
}, []);
return $result;
}
/**
* {@inheritdoc}
*/
public function getInfo() {
return [
'#children' => [],
'#element_validate' => [
[
get_class(),
'validateNumber',
],
],
'#input' => TRUE,
'#max' => NULL,
'#min' => NULL,
'#placeholder' => NULL,
'#process' => [
[
get_class(),
'processNumber',
],
],
'#required' => FALSE,
'#step' => NULL,
'#theme' => 'container',
'#theme_wrappers' => [
'form_element',
],
'#title' => NULL,
'#tree' => TRUE,
'#unit_options' => NULL,
'#unit_required' => FALSE,
'#attributes' => [
'class' => [
'container-inline',
],
],
];
}
/**
* Get an associative array of unit options for the supplied element.
*
* The supplied element's "#unit_options" property will determine the result
* of this method using the following algorithm:
*
* 1. If "#unit_options" is an array, it will be intersected with an internal
* list of allowed unit options.
* 2. Else, if "#unit_options" is a string, the desired unit will be extracted
* from an internal list of allowed unit options.
* 3. Else, if "#unit_options" is equivalent to TRUE, all allowed unit options
* will be produced.
* 4. Else, no unit options will be produced.
*
* @param array $element
* The element for which to get an associative array of unit options.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup[string]
* An associative array of unit option labels keyed by unit name.
*/
protected static function getUnitOptions(array $element) {
$allowed = self::getAllowedUnits();
$desired = $element['#unit_options'];
$result = [];
// Check whether a subset of the allowed units is desired.
if (is_array($desired)) {
$result = array_intersect_key($allowed, array_flip($desired));
}
elseif (is_string($desired) && array_key_exists($desired, $allowed)) {
$result = array_intersect_key($allowed, array_flip([
$desired,
]));
}
elseif (is_int($desired)) {
$result = self::getAllowedUnits($desired);
}
return $result;
}
/**
* Attempt to extract the value of the supplied element.
*
* The element's value must be a flat associative array with keys 'value' and
* 'unit' (int|float and string respectively), or an intermediate object of
* type \Drupal\compiler_scss\Type\Number.
*
* @param array $element
* The element for which to attempt to extract a value.
* @param string|null $key
* The key to retrieve from the element's value array or NULL to retrieve
* the entire array (default: NULL).
*
* @return array|float|int|null|string
* The requested sub-value from the supplied element, or the entire element
* value if there was no specific key requested.
*/
protected static function getValue(array $element, ?string $key = NULL) {
if (!in_array($key, [
NULL,
'unit',
'value',
], TRUE)) {
throw new \InvalidArgumentException();
}
$value = array_key_exists('#value', $element) ? $element['#value'] : [];
if ($value instanceof IntermediateNumber) {
$value = [
'value' => $value
->value(),
'unit' => $value
->unit(),
];
}
if (isset($key)) {
if (is_array($value)) {
$value = array_key_exists($key, $value) ? $value[$key] : NULL;
}
else {
$value = NULL;
}
}
return $value;
}
/**
* Initializes the unit options for this element.
*/
protected static function initializeUnits() {
self::$units = [
self::UNIT_ABSOLUTE => [
'cm' => t('centimeters'),
'in' => t('inches'),
'mm' => t('millimeters'),
'pc' => t('picas'),
'pt' => t('points'),
'px' => t('pixels'),
],
self::UNIT_ANGLE => [
'deg' => t('degrees'),
'grad' => t('gradians'),
'rad' => t('radians'),
'turn' => t('turns'),
],
self::UNIT_FREQUENCY => [
'Hz' => t('hertz'),
'kHz' => t('kilohertz'),
],
self::UNIT_PERCENT => [
'%' => t('percent'),
],
self::UNIT_RELATIVE => [
'rem' => t('× the root font size'),
'em' => t('× the element font size'),
'lh' => t('× the line height of the element'),
'ex' => t('× the x-height of the element font'),
'ch' => t('× the width of "0" in the element font'),
'vh' => t('× 1% of the viewport height'),
'vw' => t('× 1% of the viewport width'),
'vmax' => t('× 1% of the larger viewport dimension'),
'vmin' => t('× 1% of the smaller viewport dimension'),
],
self::UNIT_RESOLUTION => [
'dpcm' => t('dots per centimeter'),
'dpi' => t('dots per inch'),
'dppx' => t('dots per pixel'),
],
self::UNIT_TIME => [
'ms' => t('milliseconds'),
's' => t('seconds'),
],
];
}
/**
* Check if the element requires a value (either with or without a unit).
*
* @param array $element
* The element for which to check for a requirement preference.
*
* @return bool
* TRUE if the element requires a value, FALSE otherwise.
*/
protected static function isRequired(array $element) {
return boolval($element['#required']);
}
/**
* Check if a unit is required for the supplied element.
*
* This method checks the '#unit_required' property on the element.
* Regardless of the value of this property, a unit can only be required in
* actuality if a number has been supplied and at least one unit option is
* available for selection.
*
* @param array $element
* The element for which to check for a unit requirement preference.
*
* @return bool
* TRUE if the element requires a unit, FALSE otherwise.
*/
protected static function isUnitRequired(array $element) {
return boolval($element['#unit_required']);
}
/**
* Process the element before it gets rendered in the form.
*
* This method will ensure that the element is initialized with the following
* properties before being rendered:
*
* 1. '#required': Whether or not a value is required
* 2. '#unit_options': An array of unit options from which to select a unit
* 3. '#unit_required': Whether or not a unit is required
*
* A unit select box will only be displayed if there is at least one available
* unit option and at least one of the following is true:
*
* 1. A unit is not required (i.e., the user can opt for a unit-less value)
* 2. More than one unit option is available.
*
* If there is only one required unit, it will be stored in a hidden input and
* the unit label will be applied as a suffix to the value form element.
*
* @param array $element
* The element to process before being rendered.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form.
*
* @return array
* The resulting element after having been processed.
*/
public static function processNumber(array &$element, FormStateInterface $form_state, array &$complete_form) {
// A unit cannot be required if there are no units available to select.
if (empty($element['#unit_options'] = self::getUnitOptions($element))) {
$element['#unit_required'] = FALSE;
}
// Add a value element used to track the magnitude of the composite element.
$element['value'] = [
'#type' => 'number',
'#title' => $element['#title'],
'#title_display' => 'none',
'#name' => "{$element['#name']}[value]",
'#default_value' => self::getValue($element, 'value'),
'#max' => $element['#max'],
'#min' => $element['#min'],
'#placeholder' => $element['#placeholder'],
'#required' => $element['#required'],
'#step' => isset($element['#step']) ? $element['#step'] : 'any',
];
// Check if there is only a single, required unit available.
if ($element['#unit_required'] && count($element['#unit_options']) === 1) {
$element['value']['#field_suffix'] = end($element['#unit_options']);
self::processUnitSingle($element, $form_state, $complete_form);
}
elseif (!empty($element['#unit_options'])) {
self::processUnitMultiple($element, $form_state, $complete_form);
}
// Add references to the template '#children' property for each element.
$element['#children']['value'] =& $element['value'];
$element['#children']['unit'] =& $element['unit'];
return $element;
}
/**
* Add a unit select element to the composite form element.
*
* @param array $element
* The element to which a unit select element should be added.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form.
*
* @return array
* The resulting element after having been processed.
*/
protected static function processUnitMultiple(array &$element, FormStateInterface $form_state, array &$complete_form) {
$required = $element['#required'] && $element['#unit_required'];
$default = self::getValue($element, 'unit');
// Check if an empty option should be presented.
$empty_option = !$required || empty($default) ? [
'#empty_option' => t('- Unit -'),
] : [];
// Display a select box for the user to pick a unit (if any).
$element['unit'] = [
'#type' => 'select',
'#title' => $element['#title'],
'#title_display' => 'none',
'#name' => "{$element['#name']}[unit]",
'#required' => $required,
'#default_value' => $default,
'#options' => $element['#unit_options'],
'#states' => [
'disabled' => [
":input[name=\"{$element['value']['#name']}\"]" => [
'empty' => TRUE,
],
],
],
] + $empty_option;
return $element;
}
/**
* Add a hidden unit input to the composite form element.
*
* @param array $element
* The element to which a hidden unit input should be added.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form.
*
* @return array
* The resulting element after having been processed.
*/
protected static function processUnitSingle(array &$element, FormStateInterface $form_state, array &$complete_form) {
// Store the only possible unit in a hidden input on the form.
$element['unit'] = [
'#type' => 'hidden',
'#name' => "{$element['#name']}[unit]",
'#value' => key($element['#unit_options']),
];
return $element;
}
/**
* Validate a number element's value on form submission.
*
* If a value is required, but none was submitted, an error will be added.
*
* If a value is provided, a unit is required, but none was selected, an error
* will be added.
*
* @param array $element
* The element that should be validated.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form.
*/
public static function validateNumber(array &$element, FormStateInterface $form_state, array &$complete_form) {
// Extract the input value from the element.
$input = self::getValue($element);
// Extract the value & unit from this element's input (if available).
$value = $input['value'] ?? NULL;
$unit = $input['unit'] ?? NULL;
// Check if no value was submitted where one was required.
if (!is_numeric($value) && self::isRequired($element)) {
$error = $element['#title'] ? t('%name requires a value.', [
'%name' => $element['#title'],
]) : t('This field requires a value.');
$form_state
->setError($element['value'], $error);
}
// Determine whether a unit is required for this element.
$unit_required = is_numeric($value) && self::isUnitRequired($element);
// Check if no (valid) unit was selected where one was required.
if (!array_key_exists($unit, self::getAllowedUnits()) && $unit_required) {
$error = $element['#title'] ? t('%name requires a unit.', [
'%name' => $element['#title'],
]) : t('This field requires a unit.');
$form_state
->setError($element['unit'], $error);
}
// Check if only a unit was submitted.
if (!is_numeric($value) && !empty($unit)) {
$error = $element['#title'] ? t('%name cannot have only a unit.', [
'%name' => $element['#title'],
]) : t('This field cannot have only a unit.');
// Only complain about empty values with a unit if the unit isn't forced.
if ($element['unit']['#type'] !== 'hidden') {
$form_state
->setError($element['unit'], $error);
}
else {
$form_state
->setValueForElement($element['unit'], NULL);
}
}
// Canonicalize the element value and store it in the form state.
$unit = is_numeric($value) ? array_filter([
'unit' => $unit,
]) : [];
$value = is_numeric($value) ? [
'value' => $value,
] : [];
$form_state
->setValueForElement($element, $value + $unit ?: NULL);
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
// Populate the element with a default value if none already exists.
$element += [
'#default_value' => [],
];
// Allow typed data to be supplied.
if ($element['#default_value'] instanceof IntermediateNumber) {
$element['#default_value'] = [
'value' => $element['#default_value']
->value(),
'unit' => $element['#default_value']
->unit(),
];
}
// Allow a scalar value to be supplied instead of a value & unit pair.
if (is_scalar($element['#default_value'])) {
$element['#default_value'] = [
'value' => $element['#default_value'],
];
}
// Check if the element's default value should be used in lieu of an input.
if ($input === FALSE) {
// Replace the input with the element's default value if FALSE.
$input = $element['#default_value'];
}
// Only attempt to process the input (or default value) if it's an array.
if (is_array($input)) {
// Extract the value & unit from this element's input (if available).
$value = $input['value'] ?? NULL;
$unit = $input['unit'] ?? NULL;
// Canonicalize the input value and return it.
$unit = is_numeric($value) ? array_filter([
'unit' => $unit,
]) : [];
$value = is_numeric($value) ? [
'value' => $value,
] : [];
// Only return a value if a numeric input was supplied.
return $value + $unit ?: NULL;
}
return NULL;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
public | function | 2 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormElement:: |
public static | function | Adds autocomplete functionality to elements. | |
FormElement:: |
public static | function | #process callback for #pattern form element property. | |
FormElement:: |
public static | function | #element_validate callback for #pattern form element property. | |
MessengerTrait:: |
protected | property | The messenger. | 27 |
MessengerTrait:: |
public | function | Gets the messenger. | 27 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
2 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
public | function | Constructs a \Drupal\Component\Plugin\PluginBase object. | 98 |
RenderElement:: |
public static | function | Adds Ajax information about an element to communicate with JavaScript. | |
RenderElement:: |
public static | function | Adds members of this group as actual elements for rendering. | |
RenderElement:: |
public static | function | Form element processing handler for the #ajax form property. | 1 |
RenderElement:: |
public static | function | Arranges elements into groups. | |
RenderElement:: |
public static | function |
Sets a form element's class attribute. Overrides ElementInterface:: |
|
ScssNumber:: |
protected static | property | An array of unit groups keyed by their corresponding filter constant. | |
ScssNumber:: |
public static | function | Get an associative array of allowed unit options, keyed by unit name. | |
ScssNumber:: |
public | function |
Returns the element properties for this element. Overrides ElementInterface:: |
|
ScssNumber:: |
protected static | function | Get an associative array of unit options for the supplied element. | |
ScssNumber:: |
protected static | function | Attempt to extract the value of the supplied element. | |
ScssNumber:: |
protected static | function | Initializes the unit options for this element. | |
ScssNumber:: |
protected static | function | Check if the element requires a value (either with or without a unit). | |
ScssNumber:: |
protected static | function | Check if a unit is required for the supplied element. | |
ScssNumber:: |
public static | function | Process the element before it gets rendered in the form. | |
ScssNumber:: |
protected static | function | Add a unit select element to the composite form element. | |
ScssNumber:: |
protected static | function | Add a hidden unit input to the composite form element. | |
ScssNumber:: |
constant | Used when filtering the allowed units list to include "absolute" units. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include all supported units. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include "angle" units. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include "angle" units. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include absolute & relative. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include length & percent. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include the percent unit. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include "relative" units. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include "resolution" units. | ||
ScssNumber:: |
constant | Used when filtering the allowed units list to include "time" units. | ||
ScssNumber:: |
public static | function | Validate a number element's value on form submission. | |
ScssNumber:: |
public static | function |
Determines how user input is mapped to an element's #value property. Overrides FormElement:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 4 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |