diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index de1922835a3..b89d71d1610 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -6,6 +6,7 @@ - Added the “Affiliated Site” user condition rule. ([#16174](https://github.com/craftcms/cms/pull/16174)) - The global sidebar no longer shows “Failed” for queue jobs, for users that don’t have access to the Queue Manager. ([#16184](https://github.com/craftcms/cms/issues/16184)) - Addresses and Matrix fields now show provisional drafts when previewing an owner element. ([#16295](https://github.com/craftcms/cms/issues/16295)) +- Color fields with a predefined color palette now primarily show a color select dropdown, rather than a manual color input. ([#16249](https://github.com/craftcms/cms/pull/16249)) ### Accessibility - Improved the accessibility of Checkboxes and Radio Buttons fields that allow custom options. ([#16080](https://github.com/craftcms/cms/pull/16080)) @@ -22,6 +23,8 @@ - Added the “View users” user permission. ([#16206](https://github.com/craftcms/cms/pull/16206)) - Added the “Advanced Fields” setting to Link fields, with “Target”, “URL Suffix”, “Title Text”, “ARIA Label”, “Class Name”, “ID”, and “Relation (rel)” options. ([#15813](https://github.com/craftcms/cms/discussions/15813)) - Added the “GraphQL Mode” Link field setting. ([#16237](https://github.com/craftcms/cms/pull/16237)) +- Added the “Palette” setting to Color fields, which replaces “Presets”. ([#16249](https://github.com/craftcms/cms/pull/16249)) +- Added the “Allow custom colors” setting to Color fields. ([#16249](https://github.com/craftcms/cms/pull/16249)) - Added the “Field” entry condition rule, which replaces “Matrix field”, includes a “has a value” operator. ([#16270](https://github.com/craftcms/cms/discussions/16270)) - Section condition rules now have a “has a value” operator. ([#16270](https://github.com/craftcms/cms/discussions/16270)) - Added “Copy plugin handle” and “Copy package name” options to plugins’ action menus on the Plugins index page. ([#16281](https://github.com/craftcms/cms/discussions/16281)) @@ -49,6 +52,10 @@ - Added `craft\elements\User::$affiliatedSiteId`. - Added `craft\elements\User::getAffiliatedSite()`. - Added `craft\elements\conditions\entries\FieldConditionRule`. +- Added `craft\fields\Color::$allowCustomColors`. ([#16249](https://github.com/craftcms/cms/pull/16249)) +- Added `craft\fields\Color::$palette`. ([#16249](https://github.com/craftcms/cms/pull/16249)) +- Added `craft\fields\Color::getDefaultColor()`. ([#16249](https://github.com/craftcms/cms/pull/16249)) +- Added `craft\fields\Color::setDefaultValue()`. ([#16249](https://github.com/craftcms/cms/pull/16249)) - Added `craft\fields\data\LinkData::$ariaLabel`. - Added `craft\fields\data\LinkData::$class`. - Added `craft\fields\data\LinkData::$id`. @@ -58,6 +65,7 @@ - Added `craft\fields\data\LinkData::getUrl()`. - Added `craft\gql\types\LinkData`. - Added `craft\gql\types\generators\LinkDataType`. +- Added `craft\helpers\Cp::colorHtml()`. - Added `craft\mail\Mailer::$siteId`. - Added `craft\mail\Mailer::$siteOverrides`. - Added `craft\models\MailSettings::$siteOverrides`. @@ -67,8 +75,11 @@ - `craft\helpers\Cp::elementIndexHtml()` now supports passing `defaultSort` in the `$config` array, when `sources` is `null`. ([#16236](https://github.com/craftcms/cms/discussions/16236)) - `craft\models\Site` now implements `craft\base\Chippable`. - `craft\services\Revisions::createRevision()` no longer creates the revision if an `EVENT_BEFORE_CREATE_REVISION` event handler sets `$event->handled` to `true` and at least one revision already exists for the element. ([#16260](https://github.com/craftcms/cms/discussions/16260)) +- Deprecated `craft\fields\Color::$presets`. ([#16249](https://github.com/craftcms/cms/pull/16249)) - Deprecated `craft\fields\Link::$showTargetField`. -- Sortable checkbox selects now always display the selected options first on initial render. +- `_includes/forms/colorSelect.twig` now supports `options` and `withBlankOption` variables. +- `_includes/forms/selectize.twig` now supports a `color` property in option data, which can be set to a hex value or a color name. +- Sortable checkbox selects now always display the selected options first on initial render. ### System - Craft now keeps track of which site users registered from. When sending an email from the control panel, the current site is now set to the user’s affiliated site, if known. ([#16174](https://github.com/craftcms/cms/pull/16174)) diff --git a/src/fields/Color.php b/src/fields/Color.php index b1e6533b02d..0c23f05cb9e 100644 --- a/src/fields/Color.php +++ b/src/fields/Color.php @@ -14,14 +14,18 @@ use craft\base\MergeableFieldInterface; use craft\elements\Entry; use craft\fields\data\ColorData; +use craft\helpers\ArrayHelper; use craft\helpers\Cp; use craft\helpers\Html; +use craft\helpers\StringHelper; use craft\validators\ColorValidator; +use Illuminate\Support\Arr; use yii\db\Schema; /** * Color represents a Color field. * + * @property string|null $defaultColor * @author Pixel & Tonic, Inc. * @since 3.0.0 */ @@ -60,69 +64,143 @@ public static function dbType(): string } /** - * @var string|null The default color hex + * @var array Color palette + * @phpstan-var array{color:string,label:string|null,default:bool}[] + * @since 5.6.0 */ - public ?string $defaultColor = null; + public array $palette = []; /** - * @var string[] Preset colors - * @since 4.8.0 + * @var bool Allow custom culors + * @since 5.6.0 */ - public array $presets = []; + public bool $allowCustomColors = false; /** * @inheritdoc */ public function __construct($config = []) { - if (isset($config['presets'])) { - $config['presets'] = array_values(array_filter(array_map( - fn($color) => is_array($color) ? $color['color'] : $color, - $config['presets'] - ))); - // Normalize afterward so empty strings have been filtered out - $config['presets'] = array_map( - fn(string $color) => ColorValidator::normalizeColor($color), - $config['presets'] + // presets => palette + if (array_key_exists('presets', $config) || array_key_exists('defaultColor', $config)) { + $defaultColor = ArrayHelper::remove($config, 'defaultColor'); + $config['palette'] = array_map(fn(string $color) => [ + 'color' => $color, + 'label' => null, + 'default' => ($color === $defaultColor), + ], ArrayHelper::remove($config, 'presets') ?? []); + } + + if (isset($config['palette'])) { + $config['palette'] = array_map( + fn(array $color) => [ + 'color' => $color['color'] ? ColorValidator::normalizeColor($color['color']) : null, + ] + $color, + $config['palette'] ); } + // Default allowCustomColors to true for existing fields + if (isset($config['id']) && !isset($config['allowCustomColors'])) { + $config['allowCustomColors'] = true; + } + parent::__construct($config); } + /** + * Returns the default color + * + * @return string|null + * @since 5.6.0 + */ + public function getDefaultColor(): ?string + { + $color = Arr::first($this->palette, fn(array $color) => $color['default'] ?? false); + return $color ? $color['color'] : null; + } + + /** + * Sets the default color + * + * @param string|null $defaultColor + * @since 5.6.0 + */ + public function setDefaultValue(?string $defaultColor): void + { + $this->palette = Arr::map($this->palette, fn(array $color) => ['default' => false] + $color); + + if ($defaultColor) { + $defaultColor = ColorValidator::normalizeColor($defaultColor); + foreach ($this->palette as $color) { + if (($color['color'] ?? null) === $defaultColor) { + $color['default'] = true; + return; + } + } + } + + // If we're still here, the default color didn’t exist in the palette + $this->palette[] = ['color' => $defaultColor, 'label' => null, 'default' => true]; + } + + /** + * @return string[] + * @deprecated in 5.6.0 + */ + public function getPresets(): array + { + return array_values(array_filter(array_map(fn(array $color) => $color['color'], $this->palette))); + } + + /** + * @param string[] $presets + * @deprecated in 5.6.0 + */ + public function setPresets(array $presets): void + { + $this->palette = array_map( + fn(string $color) => ['color' => $color, 'label' => null, 'default' => false], + $presets, + ); + } + /** @inheritdoc */ public function getSettingsHtml(): ?string { - return Cp::colorFieldHtml([ - 'label' => Craft::t('app', 'Default Color'), - 'id' => 'default-color', - 'name' => 'defaultColor', - 'value' => $this->defaultColor, - 'errors' => $this->getErrors('defaultColor'), - 'data' => ['error-key' => 'defaultColor'], - ]) . + return Cp::editableTableFieldHtml([ - 'label' => Craft::t('app', 'Presets'), - 'name' => 'presets', - 'instructions' => Craft::t('app', 'Choose colors which should be recommended by the color picker.'), + 'label' => Craft::t('app', 'Palette'), + 'name' => 'palette', + 'instructions' => Craft::t('app', 'Define the available colors to choose from.'), 'cols' => [ 'color' => [ 'type' => 'color', 'heading' => Craft::t('app', 'Color'), ], + 'label' => [ + 'type' => 'singleline', + 'heading' => Craft::t('app', 'Label'), + ], + 'default' => [ + 'type' => 'checkbox', + 'heading' => Craft::t('app', 'Default'), + 'radioMode' => true, + ], ], - 'rows' => array_map(fn(string $color) => compact('color'), $this->presets), + 'rows' => $this->palette, 'allowAdd' => true, 'allowReorder' => true, 'allowDelete' => true, 'addRowLabel' => Craft::t('app', 'Add a color'), - 'inputContainerAttributes' => [ - 'style' => [ - 'max-width' => '15em', - ], - ], - 'errors' => $this->getErrors('presets'), - 'data' => ['error-key' => 'presets'], + 'errors' => $this->getErrors('palette'), + 'data' => ['error-key' => 'palette'], + ]) . + Cp::lightswitchFieldHtml([ + 'label' => Craft::t('app', 'Allow custom colors'), + 'id' => 'allow-custom-colors', + 'name' => 'allowCustomColors', + 'on' => $this->allowCustomColors, ]); } @@ -131,21 +209,26 @@ public function getSettingsHtml(): ?string */ protected function defineRules(): array { - $rules = parent::defineRules(); - $rules[] = [['defaultColor'], ColorValidator::class]; - - $rules[] = [['presets'], function() { - $validator = new ColorValidator(); - foreach ($this->presets as $color) { - if (!$validator->validate($color, $error)) { - $this->addError('presets', Craft::t('yii', '{attribute} is invalid.', [ - 'attribute' => "#$color", - ])); + return [ + ...parent::defineRules(), + [ + ['palette'], + 'required', + 'when' => fn() => !$this->allowCustomColors, + 'message' => Craft::t('app', 'Palette cannot be blank if custom colors aren’t allowed.'), + ], + [['palette'], function() { + $validator = new ColorValidator(); + foreach ($this->palette as $color) { + if (!$validator->validate($color['color'], $error)) { + $this->addError('palette', Craft::t('yii', '{attribute} is invalid.', [ + /** @phpstan-ignore-next-line */ + 'attribute' => StringHelper::ensureLeft($color['color'] ?? '', '#'), + ])); + } } - } - }]; - - return $rules; + }], + ]; } /** @@ -165,9 +248,24 @@ public function normalizeValue(mixed $value, ?ElementInterface $element): mixed return $value; } + if (is_array($value)) { + if (($value['color'] ?? null) !== '__custom__') { + $value = $value['color']; + } else { + $value = $value['custom'] ?? null; + } + } + + if ($value === '__blank__') { + return null; + } + // If this is a new entry, look for any default options - if ($value === null && $this->isFresh($element) && $this->defaultColor) { - $value = $this->defaultColor; + if ($value === null && $this->isFresh($element)) { + $defaultColor = $this->getDefaultColor(); + if ($defaultColor) { + $value = $defaultColor; + } } $value = trim($value); @@ -187,6 +285,19 @@ public function getElementValidationRules(): array { return [ ColorValidator::class, + [ + function(ElementInterface $element) { + if (!$this->allowCustomColors) { + /** @var ColorData $value */ + $value = $element->getFieldValue($this->handle); + if (!ArrayHelper::contains($this->palette, fn(array $color) => $color['color'] === $value->getHex())) { + $element->addError("field:$this->handle", Craft::t('yii', '{attribute} is invalid.', [ + 'attribute' => $this->getUiLabel(), + ])); + } + } + }, + ], ]; } @@ -195,14 +306,105 @@ public function getElementValidationRules(): array */ protected function inputHtml(mixed $value, ?ElementInterface $element, bool $inline): string { + $id = $this->getInputId(); + + if (empty($this->palette)) { + return Cp::colorHtml([ + 'id' => $id, + 'describedBy' => $this->describedBy, + 'name' => $this->handle, + 'value' => $value?->getHex(), + ]); + } + /** @var ColorData|null $value */ - return Craft::$app->getView()->renderTemplate('_includes/forms/color.twig', [ - 'id' => $this->getInputId(), - 'describedBy' => $this->describedBy, - 'name' => $this->handle, - 'value' => $value?->getHex(), - 'presets' => $this->presets, - ]); + $isInPalette = ( + $value && + ArrayHelper::contains($this->palette, fn(array $color) => $color['color'] === $value->getHex()) + ); + $isCustom = ( + $value && + $this->allowCustomColors && + !$isInPalette + ); + $showBlankOption = ( + !$value || + !$this->layoutElement->required || + (!$this->allowCustomColors && !$isInPalette) + ); + + $html = + Html::beginTag('div', [ + 'class' => ['flex', 'flex-col', 'items-stretch'], + 'style' => [ + 'width' => '25em', + 'max-width' => '100%', + ], + ]) . + Cp::colorSelectFieldHtml([ + 'id' => $id, + 'labelledBy' => $this->getLabelId(), + 'describedBy' => $this->describedBy, + 'class' => 'fullwidth', + 'name' => "$this->handle[color]", + 'options' => array_filter([ + ...array_map( + fn(array $color) => [ + 'label' => $color['label'] ?? $color['color'], + 'value' => $color['color'], + ], + $this->palette, + ), + $this->allowCustomColors ? [ + 'label' => Craft::t('app', 'Custom…'), + 'value' => '__custom__', + ] : null, + ]), + 'withBlankOption' => $showBlankOption, + 'value' => $isInPalette ? $value->getHex() : ($isCustom ? '__custom__' : '__blank__'), + 'toggle' => $this->allowCustomColors, + 'targetPrefix' => $this->allowCustomColors ? "$id-custom-" : null, + ]); + + if ($this->allowCustomColors) { + $customLabelId = "$id-custom-label"; + $html .= + Html::beginTag('div', [ + 'id' => "$id-custom-__custom__", + 'class' => array_filter([ + 'pane', + 'hairline', + 'py-s', + 'mt-0', + 'flex', + 'flex-inline', + !$isCustom ? 'hidden' : null, + ]), + 'style' => [ + 'width' => '25em', + 'max-width' => '100%', + 'padding-inline' => '9px', + ], + ]) . + Html::label(Craft::t('app', 'Custom color:'), "$id-custom-input", [ + 'id' => $customLabelId, + ]) . + Cp::colorHtml([ + 'id' => "$id-custom-input", + 'labelledBy' => $customLabelId, + 'describedBy' => $this->describedBy, + 'name' => "$this->handle[custom]", + 'value' => $isCustom ? $value->getHex() : null, + 'presets' => $this->getPresets(), + ]) . + Html::endTag('div'); + } elseif ($value && !$isInPalette) { + Craft::$app->getView()->setInitialDeltaValue($this->handle, $value->getHex()); + } + + $html .= Html::endTag('div'); + + return $html; } /** diff --git a/src/helpers/Cp.php b/src/helpers/Cp.php index 37e59f36172..b89dadf2520 100644 --- a/src/helpers/Cp.php +++ b/src/helpers/Cp.php @@ -1716,6 +1716,19 @@ public static function checkboxGroupFieldHtml(array $config): string return static::fieldHtml('template:_includes/forms/checkboxGroup.twig', $config); } + /** + * Renders a color input’s HTML. + * + * @param array $config + * @return string + * @throws InvalidArgumentException if `$config['siteId']` is invalid + * @since 5.6.0 + */ + public static function colorHtml(array $config): string + { + return static::renderTemplate('_includes/forms/color.twig', $config); + } + /** * Renders a color field’s HTML. * diff --git a/src/templates/_includes/forms/color.twig b/src/templates/_includes/forms/color.twig index 8e53fb99dab..d4fecffaaa7 100644 --- a/src/templates/_includes/forms/color.twig +++ b/src/templates/_includes/forms/color.twig @@ -2,7 +2,8 @@ {% set id = id ?? "color#{random()}" -%} {% set containerId = containerId ?? id~'-container' -%} -{% set hexLabelId = "hex-#{id}" %} +{% set hexLabelId = "#{id}-label" %} +{% set hexDescriptionId = "#{id}-desc" %} {% set name = name ?? null -%} {% set value = value ?? null -%} {% set small = small ?? false -%} @@ -32,6 +33,7 @@
{{ 'Color hex value'|t('app') }} + {{ 'Value prefixed by “{prefix}”.'|t('app', {prefix: '#'}) }} {{ forms.text({ id: id, describedBy: describedBy ?? false, @@ -42,6 +44,11 @@ autofocus: autofocus, disabled: disabled, labelledBy: [labelledBy, hexLabelId]|filter|join(' '), + inputAttributes: { + aria: { + describedby: hexDescriptionId, + } + } }) }}
{% endtag %} diff --git a/src/templates/_includes/forms/colorSelect.twig b/src/templates/_includes/forms/colorSelect.twig index 68daa1daae1..595967cb0a9 100644 --- a/src/templates/_includes/forms/colorSelect.twig +++ b/src/templates/_includes/forms/colorSelect.twig @@ -1,33 +1,39 @@ {% set id = id ?? "fs#{random()}" %} {% set value = value ?? null %} +{% set withBlankOption = withBlankOption ?? true %} + +{% set options = (options ?? [ + {label: 'Red'|t('app'), value: 'red'}, + {label: 'Orange'|t('app'), value: 'orange'}, + {label: 'Amber'|t('app'), value: 'amber'}, + {label: 'Yellow'|t('app'), value: 'yellow'}, + {label: 'Lime'|t('app'), value: 'lime'}, + {label: 'Green'|t('app'), value: 'green'}, + {label: 'Emerald'|t('app'), value: 'emerald'}, + {label: 'Teal'|t('app'), value: 'teal'}, + {label: 'Cyan'|t('app'), value: 'cyan'}, + {label: 'Sky'|t('app'), value: 'sky'}, + {label: 'Blue'|t('app'), value: 'blue'}, + {label: 'Indigo'|t('app'), value: 'indigo'}, + {label: 'Violet'|t('app'), value: 'violet'}, + {label: 'Purple'|t('app'), value: 'purple'}, + {label: 'Fuchsia'|t('app'), value: 'fuchsia'}, + {label: 'Pink'|t('app'), value: 'pink'}, + {label: 'Rose'|t('app'), value: 'rose'}, +])|map(o => not (o.value starts with('_')) ? o|merge({data: {color: o.value}}) : o) %} + +{% if withBlankOption %} + {% set options = options|unshift({label: 'No color'|t('app'), value: '__blank__', data: {color: ''}}) %} +{% endif %} {% if value is instance of ('craft\\enums\\Color') %} {% set value = value.value %} {% endif %} {% include '_includes/forms/selectize' with { - options: [ - {label: 'No color'|t('app'), value: '__blank__', data: {status: 'disabled'}}, - {label: 'Red'|t('app'), value: 'red'}, - {label: 'Orange'|t('app'), value: 'orange'}, - {label: 'Amber'|t('app'), value: 'amber'}, - {label: 'Yellow'|t('app'), value: 'yellow'}, - {label: 'Lime'|t('app'), value: 'lime'}, - {label: 'Green'|t('app'), value: 'green'}, - {label: 'Emerald'|t('app'), value: 'emerald'}, - {label: 'Teal'|t('app'), value: 'teal'}, - {label: 'Cyan'|t('app'), value: 'cyan'}, - {label: 'Sky'|t('app'), value: 'sky'}, - {label: 'Blue'|t('app'), value: 'blue'}, - {label: 'Indigo'|t('app'), value: 'indigo'}, - {label: 'Violet'|t('app'), value: 'violet'}, - {label: 'Purple'|t('app'), value: 'purple'}, - {label: 'Fuchsia'|t('app'), value: 'fuchsia'}, - {label: 'Pink'|t('app'), value: 'pink'}, - {label: 'Rose'|t('app'), value: 'rose'}, - ]|map(o => o.value ? o|merge({data: {status: o.value}}) : o), + options, selectizeOptions: { - allowEmptyOption: true, + allowEmptyOption: withBlankOption, }, - value: (value ?? '') ?: '__blank__', + value: value ?: (withBlankOption ? '__blank__' : null), } %} diff --git a/src/templates/_includes/forms/selectize.twig b/src/templates/_includes/forms/selectize.twig index 85d52f76f68..9e9e7a01859 100644 --- a/src/templates/_includes/forms/selectize.twig +++ b/src/templates/_includes/forms/selectize.twig @@ -95,6 +95,18 @@ if (status) { label += ``; } + const color = hasData(data, 'color') ? getData(data, 'color') : false; + if (color !== false) { + label += '
'; + if (color[0] == '#') { + label += `
`; + } else if (color != '__blank__') { + label += `
`; + } else { + label += `
`; + } + label += '
'; + } label += `${Craft.escapeHtml(getData(data, 'text'))}`; if (showHint && hasData(data, 'hint') && getData(data, 'hint') !== '') { const hintLang = getData(data, 'hintLang'); diff --git a/src/translations/en/app.php b/src/translations/en/app.php index a9ef3ca1502..ecff7dacc1b 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -97,6 +97,7 @@ 'All' => 'All', 'Allow Upscaling' => 'Allow Upscaling', 'Allow anchors' => 'Allow anchors', + 'Allow custom colors' => 'Allow custom colors', 'Allow custom options' => 'Allow custom options', 'Allow line breaks' => 'Allow line breaks', 'Allow public registration' => 'Allow public registration', @@ -279,7 +280,6 @@ 'Choose a site' => 'Choose a site', 'Choose a user group that publicly-registered members will be added to by default.' => 'Choose a user group that publicly-registered members will be added to by default.', 'Choose a user' => 'Choose a user', - 'Choose colors which should be recommended by the color picker.' => 'Choose colors which should be recommended by the color picker.', 'Choose how nested {type} should be presented to authors.' => 'Choose how nested {type} should be presented to authors.', 'Choose how the field should look for authors.' => 'Choose how the field should look for authors.', 'Choose the available content for querying with this schema:' => 'Choose the available content for querying with this schema:', @@ -478,6 +478,7 @@ 'Current' => 'Current', 'Currently SVG images do not support focal point.' => 'Currently SVG images do not support focal point.', 'Custom Fields' => 'Custom Fields', + 'Custom color:' => 'Custom color:', 'Custom' => 'Custom', 'Custom:' => 'Custom:', 'Customize sources' => 'Customize sources', @@ -498,7 +499,6 @@ 'Deactivating a user revokes their ability to sign in. Are you sure you want to continue?' => 'Deactivating a user revokes their ability to sign in. Are you sure you want to continue?', 'Debug Toolbar' => 'Debug Toolbar', 'Decimal Points' => 'Decimal Points', - 'Default Color' => 'Default Color', 'Default Focal Point' => 'Default Focal Point', 'Default Instructions' => 'Default Instructions', 'Default Sort' => 'Default Sort', @@ -512,6 +512,7 @@ 'Default View Mode' => 'Default View Mode', 'Default {type} Placement' => 'Default {type} Placement', 'Default?' => 'Default?', + 'Define the available colors to choose from.' => 'Define the available colors to choose from.', 'Define the available options.' => 'Define the available options.', 'Define the columns your table should have.' => 'Define the columns your table should have.', 'Define the default values for the field.' => 'Define the default values for the field.', @@ -1150,6 +1151,8 @@ 'Page Not Found' => 'Page Not Found', 'Page not found.' => 'Page not found.', 'Pagination' => 'Pagination', + 'Palette cannot be blank if custom colors aren’t allowed.' => 'Palette cannot be blank if custom colors aren’t allowed.', + 'Palette' => 'Palette', 'Parent {type} Title' => 'Parent {type} Title', 'Parent' => 'Parent', 'Parish' => 'Parish', @@ -1216,7 +1219,6 @@ 'Prefix Text' => 'Prefix Text', 'Prefix must be 5 or less characters long.' => 'Prefix must be 5 or less characters long.', 'Prefix' => 'Prefix', - 'Presets' => 'Presets', 'Prettify query' => 'Prettify query', 'Prettify' => 'Prettify', 'Prev' => 'Prev', diff --git a/src/web/assets/cp/dist/cp.js b/src/web/assets/cp/dist/cp.js index 00a4ced92df..b78e282e789 100644 --- a/src/web/assets/cp/dist/cp.js +++ b/src/web/assets/cp/dist/cp.js @@ -1,3 +1,3 @@ /*! For license information please see cp.js.LICENSE.txt */ -(function(){var __webpack_modules__={463:function(){Craft.Accordion=Garnish.Base.extend({$trigger:null,targetSelector:null,_$target:null,init:function(t){var e=this;this.$trigger=$(t),this.$trigger.data("accordion")&&(console.warn("Double-instantiating an accordion trigger on an element"),this.$trigger.data("accordion").destroy()),this.$trigger.data("accordion",this),this.targetSelector=this.$trigger.attr("aria-controls")?"#".concat(this.$trigger.attr("aria-controls")):null,this.targetSelector&&(this._$target=$(this.targetSelector)),this.addListener(this.$trigger,"click","onTriggerClick"),this.addListener(this.$trigger,"keypress",(function(t){var n=t.keyCode;n!==Garnish.SPACE_KEY&&n!==Garnish.RETURN_KEY||(t.preventDefault(),e.onTriggerClick())}))},onTriggerClick:function(){"true"===this.$trigger.attr("aria-expanded")?this.hideTarget(this._$target):this.showTarget(this._$target)},showTarget:function(t){var e=this;if(t&&t.length){this.showTarget._currentHeight=t.height(),t.removeClass("hidden"),this.$trigger.removeClass("collapsed").addClass("expanded").attr("aria-expanded","true");for(var n=0;n=this.settings.maxItems)){var e=$(t).appendTo(this.$tbody),n=e.find(".delete");this.settings.sortable&&this.sorter.addItems(e),this.$deleteBtns=this.$deleteBtns.add(n),this.addListener(n,"click","handleDeleteBtnClick"),this.totalItems++,this.updateUI()}},reorderItems:function(){var t=this;if(this.settings.sortable){for(var e=[],n=0;n=this.settings.maxItems?$(this.settings.newItemBtnSelector).addClass("hidden"):$(this.settings.newItemBtnSelector).removeClass("hidden"))}},{defaults:{tableSelector:null,noItemsSelector:null,newItemBtnSelector:null,idAttribute:"data-id",nameAttribute:"data-name",sortable:!1,allowDeleteAll:!0,minItems:0,maxItems:null,reorderAction:null,deleteAction:null,reorderSuccessMessage:Craft.t("app","New order saved."),reorderFailMessage:Craft.t("app","Couldn’t save new order."),confirmDeleteMessage:Craft.t("app","Are you sure you want to delete “{name}”?"),deleteSuccessMessage:Craft.t("app","“{name}” deleted."),deleteFailMessage:Craft.t("app","Couldn’t delete “{name}”."),onReorderItems:$.noop,onDeleteItem:$.noop}})},6872:function(){Craft.AssetImageEditor=Garnish.Modal.extend({$body:null,$footer:null,$imageTools:null,$buttons:null,$cancelBtn:null,$replaceBtn:null,$saveBtn:null,$focalPointBtn:null,$editorContainer:null,$straighten:null,$croppingCanvas:null,$spinner:null,$constraintContainer:null,$constraintRadioInputs:null,$customConstraints:null,canvas:null,image:null,viewport:null,focalPoint:null,focalPointInnerCircle:null,focalPointOuterCircle:null,focalPointPickedIndicator:null,grid:null,croppingCanvas:null,clipper:null,croppingRectangle:null,cropperHandles:null,cropperGrid:null,croppingShade:null,imageStraightenAngle:0,viewportRotation:0,originalWidth:0,originalHeight:0,imageVerticeCoords:null,zoomRatio:1,animationInProgress:!1,currentView:"",assetId:null,cacheBust:null,draggingCropper:!1,scalingCropper:!1,handleClicked:!1,draggingFocal:!1,focalPickedUp:!1,focalClicked:!1,cropperClicked:!1,previousMouseX:0,previousMouseY:0,shiftKeyHeld:!1,editorHeight:0,editorWidth:0,cropperState:!1,scaleFactor:1,flipData:{},focalPointState:!1,maxImageSize:null,lastLoadedDimensions:null,imageIsLoading:!1,mouseMoveEvent:null,croppingConstraint:!1,constraintOrientation:"landscape",showingCustomConstraint:!1,saving:!1,renderImage:null,renderCropper:null,_queue:null,init:function(t,e){var n=this;this._queue=new Craft.Queue,this.cacheBust=Date.now(),this.setSettings(e,Craft.AssetImageEditor.defaults),null===this.settings.allowDegreeFractions&&(this.settings.allowDegreeFractions=Craft.isImagick),Garnish.prefersReducedMotion()&&(this.settings.animationDuration=1),this.assetId=t,this.flipData={x:0,y:0},this.$container=$('').appendTo(Garnish.$bod),this.$body=$('
').appendTo(this.$container),this.$footer=$('