diff --git a/src/base/Element.php b/src/base/Element.php index dd313702316..f5bf14b9e83 100644 --- a/src/base/Element.php +++ b/src/base/Element.php @@ -4293,6 +4293,14 @@ public function getThumbHtml(int $size): ?string return null; } + /** + * @inheritdoc + */ + public function getThumbAlignment(): string + { + return $this->getFieldLayout()?->getCardThumbAlignment() ?? 'end'; + } + /** * Returns the URL to the element’s thumbnail, if it has one. * diff --git a/src/base/Thumbable.php b/src/base/Thumbable.php index 753068be03f..02c77032e39 100644 --- a/src/base/Thumbable.php +++ b/src/base/Thumbable.php @@ -23,4 +23,12 @@ interface Thumbable * @return string|null */ public function getThumbHtml(int $size): ?string; + + /** + * Returns the alignment in which the component's thumbnail should show. + * + * @return string + * @since 5.8.0 + */ + public function getThumbAlignment(): string; } diff --git a/src/controllers/FieldsController.php b/src/controllers/FieldsController.php index 08758ab0481..754c9830a15 100644 --- a/src/controllers/FieldsController.php +++ b/src/controllers/FieldsController.php @@ -611,6 +611,7 @@ public function actionRenderCardPreview() $fieldLayoutConfig = $this->request->getRequiredBodyParam('fieldLayoutConfig'); $cardElements = $this->request->getRequiredBodyParam('cardElements'); $showThumb = $this->request->getBodyParam('showThumb', false); + $thumbAlignment = $this->request->getBodyParam('thumbAlignment', false); if (!isset($fieldLayoutConfig['id'])) { $fieldLayout = Craft::createObject([ @@ -630,6 +631,8 @@ public function actionRenderCardPreview() array_column($cardElements, 'value') ); // this fully takes care of attributes, but not fields + $fieldLayout->setCardThumbAlignment($thumbAlignment); + return $this->asJson([ 'previewHtml' => Cp::cardPreviewHtml($fieldLayout, $cardElements, $showThumb), ]); diff --git a/src/helpers/Cp.php b/src/helpers/Cp.php index 34a38ce5198..664e58070eb 100644 --- a/src/helpers/Cp.php +++ b/src/helpers/Cp.php @@ -660,6 +660,15 @@ public static function elementCardHtml(ElementInterface $element, array $config $classes[] = 'error'; } + $thumb = $element->getThumbHtml(120); + if ($thumb) { + $thumbAlignment = $element->getThumbAlignment(); + + if ($thumbAlignment) { + $classes[] = 'thumb-' . $thumbAlignment; + } + } + $attributes = ArrayHelper::merge( self::baseElementAttributes($element, $config), [ @@ -732,7 +741,6 @@ public static function elementCardHtml(ElementInterface $element, array $config Html::endTag('div'); } - $thumb = $element->getThumbHtml(120); $icon = $element instanceof Iconic ? $element->getIcon() : null; $title = $element->getCardTitle(); @@ -2001,7 +2009,7 @@ public static function rangeHtml(array $config): string } /** - * Renders a lightswitch field’s HTML. + * Renders a range field’s HTML. * * @param array $config * @return string @@ -2014,6 +2022,33 @@ public static function rangeFieldHtml(array $config): string return static::fieldHtml('template:_includes/forms/range.twig', $config); } + /** + * Renders a button group input’s HTML. + * + * @param array $config + * @return string + * @throws TemplateLoaderException + * @since 5.8.0 + */ + public static function buttonGroupHtml(array $config): string + { + return static::renderTemplate('_includes/forms/buttonGroup.twig', $config); + } + + /** + * Renders a button group field’s HTML. + * + * @param array $config + * @return string + * @throws TemplateLoaderException + * @since 5.8.0 + */ + public static function buttonGroupFieldHtml(array $config): string + { + $config['id'] ??= 'buttongroup' . mt_rand(); + return static::fieldHtml('template:_includes/forms/buttonGroup.twig', $config); + } + /** * Renders a money input’s HTML. * @@ -2745,9 +2780,96 @@ public static function cardViewDesignerHtml(FieldLayout $fieldLayout, array $con Html::endTag('div') . // .cvd-preview-container Html::endTag('div') . // .cvd-preview Html::endTag('div') . // .cvd-container + self::_getThumbManagementHtml($fieldLayout, $config) . Html::endTag('div'); // .card-view-designer } + /** + * Return HTML for managing thumbnail provider and position. + * + * @param FieldLayout $fieldLayout + * @param array $config + * @return string + * @throws TemplateLoaderException + */ + private static function _getThumbManagementHtml(FieldLayout $fieldLayout, array $config): string + { + $readOnly = isset($config['config']['disabled']) && $config['config']['disabled']; + if ((new ($fieldLayout['type']))->hasThumbs()) { + $options = [ + ['label' => Craft::t('app', 'Default'), 'value' => '__default__'], + ]; + } else { + $options = [ + ['label' => Craft::t('app', 'No thumbnail'), 'value' => '__none__'], + ]; + } + $elementThumbnail = $fieldLayout->getThumbField()?->uid; + $thumbnailAlignment = $fieldLayout->getCardThumbAlignment(); + + $thumbableElements = array_filter( + $fieldLayout->getAllElements(), + fn($element) => $element instanceof BaseField && $element->thumbable() + ); + + foreach ($thumbableElements as $thumbableElement) { + $options[] = ['label' => $thumbableElement->label(), 'value' => $thumbableElement->uid]; + } + + $thumbHtml = Html::beginTag('div', ['class' => 'thumb-management']) . + Html::tag('h2', Craft::t('app', 'Manage element thumbnails'), ['class' => 'visually-hidden']) . + Html::beginTag('div', ['class' => 'flex flex-nowrap']); + + // dropdown field that contains all thumbable fields + 'No thumbnail' option + $thumbHtml .= self::selectFieldHtml([ + 'label' => Craft::t('app', 'Use for element thumbnails'), + 'id' => 'thumb-source', + 'name' => 'thumbSource', + 'options' => $options, + 'value' => $elementThumbnail, + 'disabled' => $readOnly, + ]); + + // radio button switch that lets you choose whether the thumb alignment should be start or end + $orientation = Craft::$app->getLocale()->getOrientation(); + $thumbHtml .= self::buttonGroupFieldHtml([ + 'label' => Craft::t('app', 'Thumbnail alignment'), + 'id' => 'thumb-alignment', + 'fieldClass' => $elementThumbnail === null ? 'hidden' : false, + 'name' => 'thumbAlignment', + 'options' => [ + [ + 'icon' => $orientation == 'ltr' ? 'slideout-left' : 'slideout-right', + 'value' => 'start', + 'attributes' => [ + 'title' => $orientation == 'ltr' ? Craft::t('app', 'Left') : Craft::t('app', 'Right'), + 'aria' => [ + 'label' => $orientation == 'ltr' ? Craft::t('app', 'Left') : Craft::t('app', 'Right'), + ], + ], + ], + [ + 'icon' => $orientation == 'ltr' ? 'slideout-right' : 'slideout-left', + 'value' => 'end', + 'attributes' => [ + 'title' => $orientation == 'ltr' ? Craft::t('app', 'Right') : Craft::t('app', 'Left'), + 'aria' => [ + 'label' => $orientation == 'ltr' ? Craft::t('app', 'Right') : Craft::t('app', 'Left'), + ], + ], + ], + ], + 'value' => $thumbnailAlignment, + 'disabled' => $readOnly, + ]); + + + $thumbHtml .= Html::endTag('div') . // .flex + Html::endTag('div'); // .thumb-management + + return $thumbHtml; + } + /** * Returns HTML for the card preview based on selected fields and attributes. * @@ -2758,6 +2880,9 @@ public static function cardViewDesignerHtml(FieldLayout $fieldLayout, array $con */ public static function cardPreviewHtml(FieldLayout $fieldLayout, array $cardElements = [], $showThumb = false): string { + $hasThumb = $showThumb ?? $fieldLayout->getThumbField() !== null ? true : (new ($fieldLayout['type']))->hasThumbs(); + $thumbAlignment = $fieldLayout->getCardThumbAlignment(); + // get heading $heading = Html::tag('craft-element-label', Html::tag('a', Html::tag('span', Craft::t('app', 'Title')), [ @@ -2777,7 +2902,7 @@ public static function cardPreviewHtml(FieldLayout $fieldLayout, array $cardElem $previewHtml = Html::beginTag('div', [ - 'class' => ['element', 'card'], + 'class' => ['element', 'card', $hasThumb ? 'thumb-' . $thumbAlignment : null], ]); $previewHtml .= @@ -2816,7 +2941,7 @@ public static function cardPreviewHtml(FieldLayout $fieldLayout, array $cardElem Html::endTag('div'); // .card-content // get thumb placeholder - if ($showThumb ?? $fieldLayout->getThumbField() !== null) { + if ($hasThumb) { $previewThumb = Html::tag('div', Html::tag('div', Cp::iconSvg('image'), ['class' => 'cp-icon']), ['class' => 'cvd-thumbnail'] @@ -2892,6 +3017,7 @@ public static function fieldLayoutDesignerHtml(FieldLayout $fieldLayout, array $ 'customizableTabs' => $config['customizableTabs'], 'customizableUi' => $config['customizableUi'], 'withCardViewDesigner' => $config['withCardViewDesigner'] ?? false, + 'alwaysShowThumbAlignmentBtns' => (new ($fieldLayout['type']))->hasThumbs(), 'readOnly' => $readOnly, ]); $namespacedId = $view->namespaceInputId($config['id']); diff --git a/src/models/FieldLayout.php b/src/models/FieldLayout.php index b148442f2b9..3c93da9ec6c 100644 --- a/src/models/FieldLayout.php +++ b/src/models/FieldLayout.php @@ -253,6 +253,13 @@ public static function createFromConfig(array $config): self */ private array $_cardView; + /** + * @var string + * @see getCardThumbAlignment() + * @see setCardThumbAlignment() + */ + private string $_cardThumbAlignment; + /** * @inheritdoc */ @@ -276,6 +283,10 @@ public function init(): void $this->setCardView([]); } } + + if (!isset($this->_cardThumbAlignment)) { + $this->setCardThumbAlignment(); + } } /** @@ -422,6 +433,38 @@ public function setCardView(?array $items): void $this->reset(); } + /** + * Returns the layout’s card view thumbnail alignment. + * + * @return string The layout’s card view thumbnail alignment. + * @since 5.8.0 + */ + public function getCardThumbAlignment(): string + { + if (!isset($this->_cardThumbAlignment)) { + $this->setCardThumbAlignment(); + } + + return $this->_cardThumbAlignment; + } + + /** + * Sets the layout’s card view thumbnail alignment. + * + * @param string|null $alignment The alignment of the card view thumbnail. + * @since 5.8.0 + */ + public function setCardThumbAlignment(?string $alignment = null): void + { + $validOptions = ['start', 'end']; + + if (!in_array($alignment, $validOptions)) { + $alignment = null; + } + + $this->_cardThumbAlignment = $alignment ?? 'end'; + } + /** * Returns the available fields, grouped by field group name. * @@ -587,14 +630,17 @@ public function getConfig(): ?array )); $cardViewConfig = $this->getCardView(); + $cardThumbAlignment = $this->getCardThumbAlignment(); if (empty($tabConfigs) && empty($cardViewConfig)) { + // no point bothering with the thumb alignment if we don't have the card view return null; } return [ 'tabs' => $tabConfigs, 'cardView' => $cardViewConfig, + 'cardThumbAlignment' => $cardThumbAlignment, ]; } diff --git a/src/services/Fields.php b/src/services/Fields.php index b02a1579095..62b9cf10734 100644 --- a/src/services/Fields.php +++ b/src/services/Fields.php @@ -1113,6 +1113,7 @@ public function assembleLayoutFromPost(?string $namespace = null): FieldLayout $config = JsonHelper::decode(Craft::$app->getRequest()->getBodyParam($paramPrefix . 'fieldLayout')); $cardView = Craft::$app->getRequest()->getBodyParam($paramPrefix . 'cardView'); $config['cardView'] = empty($cardView) ? null : $cardView; + $config['cardThumbAlignment'] = Craft::$app->getRequest()->getBodyParam($paramPrefix . 'thumbAlignment'); $layout = $this->createLayout($config); // Make sure all the elements have a dateAdded value set diff --git a/src/templates/_includes/forms/button.twig b/src/templates/_includes/forms/button.twig index 365ac25e519..cacbfd091b5 100644 --- a/src/templates/_includes/forms/button.twig +++ b/src/templates/_includes/forms/button.twig @@ -7,13 +7,17 @@ {% set icon = icon ?? null %} {% set label = label ?? null %} {% set labelHtml = labelHtml ?? null %} + +{# the "disabled" class takes care of disabling the button, + but the "read-only" class is needed so that it is allowed to have custom tabindex attribute #} {% set attributes = { type: type ?? 'button', id: id ?? false, class: (class ?? [])|explodeClass|merge([ 'btn', not (icon or label or labelHtml) ? 'btn-empty' : null, - (disabled ?? false) ? 'disabled', + (disabled ?? readOnly ?? false) ? 'disabled', + (readOnly ?? false) ? 'read-only', ]|filter), data: { 'busy-message': busyMessage, diff --git a/src/templates/_includes/forms/buttonGroup.twig b/src/templates/_includes/forms/buttonGroup.twig index 423cbcaf4f1..86923ac1ba7 100644 --- a/src/templates/_includes/forms/buttonGroup.twig +++ b/src/templates/_includes/forms/buttonGroup.twig @@ -36,7 +36,9 @@ data: { value: option.value ?? null, }, + tabindex: disabled ? '-1' : '0', }|merge(option.attributes ?? {}, recursive=true), + readOnly: disabled, }) }} {% endfor %} {% endtag %} @@ -54,6 +56,7 @@ onChange: (selectedOption) => { $('#{{ inputId|namespaceInputId }}').val(selectedOption.data('value')); }, + readOnly: {{ disabled ? 1 : 0 }} }); })(); {% endjs %} diff --git a/src/translations/en/app.php b/src/translations/en/app.php index 97f44abc7fe..b6619923b5d 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -972,6 +972,7 @@ 'Make sure you’ve followed the Environment Setup instructions before applying project config YAML changes.' => 'Make sure you’ve followed the Environment Setup instructions before applying project config YAML changes.', 'Make this the primary site' => 'Make this the primary site', 'Manage categories' => 'Manage categories', + 'Manage element thumbnails' => 'Manage element thumbnails', 'Manage your Craft Console account' => 'Manage your Craft Console account', 'Manipulated SVG image rasterizing is unreliable. See \\craft\\services\\Images::loadImage()' => 'Manipulated SVG image rasterizing is unreliable. See \\craft\\services\\Images::loadImage()', 'Matrix' => 'Matrix', @@ -1115,6 +1116,7 @@ 'No sources exist yet.' => 'No sources exist yet.', 'No tag groups exist yet.' => 'No tag groups exist yet.', 'No template path has been chosen yet.' => 'No template path has been chosen yet.', + 'No thumbnail' => 'No thumbnail', 'No usages' => 'No usages', 'No user groups exist yet.' => 'No user groups exist yet.', 'No volumes exist yet.' => 'No volumes exist yet.', @@ -1793,6 +1795,7 @@ 'This {type} doesn’t have revisions.' => 'This {type} doesn’t have revisions.', 'This {type} has been edited.' => 'This {type} has been edited.', 'This {type} has been updated.' => 'This {type} has been updated.', + 'Thumbnail alignment' => 'Thumbnail alignment', 'Time Zone' => 'Time Zone', 'Time fields are better suited for managing Time-only values.' => 'Time fields are better suited for managing Time-only values.', 'Time to reserve' => 'Time to reserve', diff --git a/src/web/assets/cp/dist/cp.js b/src/web/assets/cp/dist/cp.js index abb00bc161c..05f605a6ec6 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__={0:function(){function t(t,n){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=n){var r,i,a,s,o=[],l=!0,c=!1;try{if(a=(n=n.call(t)).next,0===e){if(Object(n)!==n)return;l=!1}else for(;!(l=(r=a.call(n)).done)&&(o.push(r.value),o.length!==e);l=!0);}catch(t){c=!0,i=t}finally{try{if(!l&&null!=n.return&&(s=n.return(),Object(s)!==s))return}finally{if(c)throw i}}return o}}(t,n)||function(t,n){if(t){if("string"==typeof t)return e(t,n);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?e(t,n):void 0}}(t,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);nn.settings.maxFileSize&&(n._rejectedFiles.size.push("“"+t.name+"”"),i=!1),i&&"function"==typeof n.settings.canAddMoreFiles&&!n.settings.canAddMoreFiles(n._validFileCounter)&&(n._rejectedFiles.limit.push("“"+t.name+"”"),i=!1),i&&(n._validFileCounter++,e.submit()),++n._totalFileCounter===e.originalFiles.length&&(n._totalFileCounter=0,n._validFileCounter=0,n.processErrorMessages())})),!0},destroy:function(){var e=this;this.uploader.fileupload("instance")&&this.uploader.fileupload("destroy"),this.$element.off("fileuploadadd",this._onFileAdd),Object.entries(this.events).forEach((function(n){var r=t(n,2),i=r[0],a=r[1];e.$element.off(i,a)}))}},{defaults:{autoUpload:!1,sequentialUploads:!0,maxFileSize:Craft.maxUploadSize,replaceFileInput:!1,createAction:"assets/upload",replaceAction:"assets/replace-file",deleteAction:"assets/delete-asset"}})},9:function(){Craft.Structure=Garnish.Base.extend({id:null,$container:null,state:null,structureDrag:null,init:function(t,e,n){this.id=t,this.$container=$(e),this.setSettings(n,Craft.Structure.defaults),this.$container.data("structure")&&(console.warn("Double-instantiating a structure on an element"),this.$container.data("structure").destroy()),this.$container.data("structure",this),this.state={},this.settings.storageKey&&$.extend(this.state,Craft.getLocalStorage(this.settings.storageKey,{})),void 0===this.state.collapsedElementIds&&(this.state.collapsedElementIds=[]);for(var r=this.$container.find("ul").prev(".row"),i=0;i').prependTo(a);-1!==$.inArray(a.children(".element").data("id"),this.state.collapsedElementIds)&&s.addClass("collapsed"),this.initToggle(o)}this.settings.sortable&&(this.structureDrag=new Craft.StructureDrag(this,this.settings.maxLevels)),this.settings.newChildUrl&&this.initNewChildMenus(this.$container.find(".add"))},initToggle:function(t){var e=this;t.on("click",(function(t){var n=$(t.currentTarget).closest("li"),r=n.children(".row").find(".element:first").data("id"),i=$.inArray(r,e.state.collapsedElementIds);n.hasClass("collapsed")?(n.removeClass("collapsed"),-1!==i&&e.state.collapsedElementIds.splice(i,1)):(n.addClass("collapsed"),-1===i&&e.state.collapsedElementIds.push(r)),e.settings.storageKey&&Craft.setLocalStorage(e.settings.storageKey,e.state)}))},initNewChildMenus:function(t){this.addListener(t,"click","onNewChildMenuClick")},onNewChildMenuClick:function(t){var e=$(t.currentTarget);if(!e.data("menubtn")){var n=e.parent().children(".element").data("id"),r=Craft.getUrl(this.settings.newChildUrl,"parentId="+n);$('").insertAfter(e),new Garnish.MenuBtn(e).showMenu()}},getIndent:function(t){return Craft.Structure.baseIndent+(t-1)*Craft.Structure.nestedIndent},addElement:function(t){var e=$('
  • ').appendTo(this.$container),n=$('
    ').appendTo(e);if(n.append(t),this.settings.sortable&&(n.append(''),this.structureDrag.addItems(e)),this.settings.newChildUrl){var r=$('').appendTo(n);this.initNewChildMenus(r)}n.css("margin-bottom",-30),n.velocity({"margin-bottom":0},"fast")},removeElement:function(t){var e,n=this,r=t.parent().parent();this.settings.sortable&&this.structureDrag.removeItems(r),r.siblings().length||(e=r.parent()),r.css("visibility","hidden").velocity({marginBottom:-r.height()},"fast",(function(){r.remove(),void 0!==e&&n._removeUl(e)}))},_removeUl:function(t){t.siblings(".row").children(".toggle").remove(),t.remove()},destroy:function(){this.$container.removeData("structure"),this.base()}},{baseIndent:8,nestedIndent:35,defaults:{storageKey:null,sortable:!1,newChildUrl:null,maxLevels:null}})},258: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=$('
  • ").appendTo(r);var a=new Garnish.MenuBtn(this.$selectTransformBtn,{onOptionSelect:this.onSelectTransform.bind(this)});a.disable(),this.$selectTransformBtn.data("menuButton",a)}},onSelectionChange:function(t){var e=!1;this.elementIndex.getSelectedElements().length&&this.settings.transforms.length&&(e=!0);var n=null;this.$selectTransformBtn&&(n=this.$selectTransformBtn.data("menuButton")),e?(n&&n.enable(),this.$selectTransformBtn.removeClass("disabled")):this.$selectTransformBtn&&(n&&n.disable(),this.$selectTransformBtn.addClass("disabled")),this.base()},onSelectTransform:function(t){var e=$(t).data("transform");this.selectImagesWithTransform(e)},selectImagesWithTransform:function(t){var e=this;void 0===Craft.AssetSelectorModal.transformUrls[t]&&(Craft.AssetSelectorModal.transformUrls[t]={});for(var n=this.elementIndex.getSelectedElements(),r=[],i=0;i=0;--i){var s=this.tryEntries[i],o=s.completion;if("root"===s.tryLoc)return r("end");if(s.tryLoc<=this.prev){var l=a.call(s,"catchLoc"),c=a.call(s,"finallyLoc");if(l&&c){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&a.call(r,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),P(n),b}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;P(n)}return i}}throw Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:M(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=e),b}},r}function r(t){return function(t){if(Array.isArray(t))return a(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||i(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){if(t){if("string"==typeof t)return a(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(t,e):void 0}}function a(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n').appendTo(Garnish.$bod);this.$sidebar=$('
    ').appendTo(r).attr({role:"navigation","aria-label":Craft.t("app","Source")}),this.$sourcesContainer=$('
    ').appendTo(this.$sidebar),this.$sourceSettingsContainer=$('
    ').appendTo(r),this.$footer=$('
    ").appendTo(i),s=$('
    ').appendTo(a),o=$("
    ').appendTo(this.$container),this.$cursor=$('
    ').appendTo(this.$container),this.$graduations=$('
    ').appendTo(this.$container),this.$graduationsUl=$("
      ").attr({"aria-hidden":"true"}).appendTo(this.$graduations),this.$container.attr({role:"slider",tabindex:"0","aria-valuemin":this.slideMin,"aria-valuemax":this.slideMax,"aria-valuenow":"0","aria-valuetext":Craft.t("app","{num, number} {num, plural, =1{degree} other{degrees}}",{num:0})});for(var r=this.graduationsMin;r<=this.graduationsMax;r++){var i=$('
    • '+r+"
    • ").appendTo(this.$graduationsUl);r%5==0&&i.addClass("main-graduation"),0===r&&i.addClass("selected")}this.$options=this.$container.find(".graduation"),this.addListener(this.$container,"resize",this._handleResize.bind(this)),this.addListener(this.$container,"tapstart",this._handleTapStart.bind(this)),this.addListener(Garnish.$bod,"tapmove",this._handleTapMove.bind(this)),this.addListener(Garnish.$bod,"tapend",this._handleTapEnd.bind(this)),this.addListener(this.$container,"keydown",this._handleKeypress.bind(this)),setTimeout((function(){n.graduationsCalculatedWidth=10*(n.$options.length-1),n.$graduationsUl.css("left",-n.graduationsCalculatedWidth/2+n.$container.width()/2)}),50)},_handleResize:function(){var t=this.valueToPosition(this.value);this.$graduationsUl.css("left",t)},_handleKeypress:function(t){var e=parseInt(this.$container.attr("aria-valuenow"),10);switch(t.keyCode){case Garnish.UP_KEY:case Garnish.RIGHT_KEY:this.setValue(e+1);break;case Garnish.DOWN_KEY:case Garnish.LEFT_KEY:this.setValue(e-1);break;case Garnish.PAGE_UP_KEY:this.setValue(e+10);break;case Garnish.PAGE_DOWN_KEY:this.setValue(e-10);break;case Garnish.HOME_KEY:this.setValue(this.slideMin);break;case Garnish.END_KEY:this.setValue(this.slideMax)}this.onChange()},_handleTapStart:function(t,e){t.preventDefault(),this.rotateIntent=$(t.target).is(".graduations *"),this.rotateIntent&&(this.startPositionX=e.position.x,this.startLeft=this.$graduationsUl.position().left,this.onStart())},_handleTapMove:function(t,e){this.rotateIntent&&Math.abs(e.position.x-this.startPositionX)>this.sensitivity&&(this.dragging=!0,this.$container.addClass("dragging"),t.preventDefault(),this._setValueFromTouch(e),this.onChange())},_setValueFromTouch:function(t){var e,n=this.dragging?this.startPositionX:this.$cursor.offset().left+this.$cursor.outerWidth()/2;e=this.dragging?n-t.position.x:t.position.x-n;var r=this.startLeft-e,i=this.positionToValue(r);this.setValue(i)},setValue:function(t){var e=this.valueToPosition(t);tthis.slideMax&&(t=this.slideMax,e=this.valueToPosition(t)),this.$graduationsUl.css("left",e),t>=this.slideMin&&t<=this.slideMax&&(this.$options.removeClass("selected"),$.each(this.$options,(function(e,n){$(n).data("graduation")>0&&$(n).data("graduation")<=t&&$(n).addClass("selected"),$(n).data("graduation")<0&&$(n).data("graduation")>=t&&$(n).addClass("selected"),0==$(n).data("graduation")&&$(n).addClass("selected")}))),this.$container.attr({"aria-valuenow":t,"aria-valuetext":Craft.t("app","{num, number} {num, plural, =1{degree} other{degrees}}",{num:parseInt(t,10)})}),this.value=t},_handleTapEnd:function(t,e){this.rotateIntent&&(this.dragging?(t.preventDefault(),this.dragging=!1,this.$container.removeClass("dragging")):(this._setValueFromTouch(e),this.onChange()),this.onEnd(),this.startPositionX=null,this.rotateIntent=!1)},positionToValue:function(t){var e=-1*this.graduationsMin,n=-1*(this.graduationsMin-this.graduationsMax);return(this.$graduations.width()/2+-1*t)/this.graduationsCalculatedWidth*n-e},valueToPosition:function(t){var e=-1*this.graduationsMin,n=-1*(this.graduationsMin-this.graduationsMax);return-((t+e)*this.graduationsCalculatedWidth/n-this.$graduations.width()/2)},onStart:function(){"function"==typeof this.settings.onChange&&this.settings.onStart(this)},onChange:function(){"function"==typeof this.settings.onChange&&this.settings.onChange(this)},onEnd:function(){"function"==typeof this.settings.onChange&&this.settings.onEnd(this)},defaultSettings:{onStart:$.noop,onChange:$.noop,onEnd:$.noop}})},3254:function(){},3517:function(){Craft.BaseUploader=Garnish.Base.extend({allowedKinds:null,$element:null,$fileInput:null,settings:null,fsType:null,formData:{},events:{},_rejectedFiles:{},_extensionList:null,_inProgressCounter:0,init:function(t,e){this._rejectedFiles={size:[],type:[],limit:[]},this.$element=t,this.settings=$.extend({},Craft.BaseUploader.defaults,e),this.formData=this.settings.formData,this.$fileInput=this.settings.fileInput||t,this.events=this.settings.events,this.settings.url||(this.settings.url=this.settings.replace?Craft.getActionUrl(this.settings.replaceAction):Craft.getActionUrl(this.settings.createAction)),this.settings.allowedKinds&&this.settings.allowedKinds.length&&("string"==typeof this.settings.allowedKinds&&(this.settings.allowedKinds=[this.settings.allowedKinds]),this.allowedKinds=this.settings.allowedKinds,delete this.settings.allowedKinds)},setParams:function(t){void 0!==Craft.csrfTokenName&&void 0!==Craft.csrfTokenValue&&(t[Craft.csrfTokenName]=Craft.csrfTokenValue),this.formData=t},getInProgress:function(){return this._inProgressCounter},isLastUpload:function(){return this.getInProgress()<2},processErrorMessages:function(){var t;this._rejectedFiles.type.length&&(t=1===this._rejectedFiles.type.length?"The file {files} could not be uploaded. The allowed file kinds are: {kinds}.":"The files {files} could not be uploaded. The allowed file kinds are: {kinds}.",t=Craft.t("app",t,{files:this._rejectedFiles.type.join(", "),kinds:this.allowedKinds.join(", ")}),this._rejectedFiles.type=[],Craft.cp.displayError(t)),this._rejectedFiles.size.length&&(t=1===this._rejectedFiles.size.length?"The file {files} could not be uploaded, because it exceeds the maximum upload size of {size}.":"The files {files} could not be uploaded, because they exceeded the maximum upload size of {size}.",t=Craft.t("app",t,{files:this._rejectedFiles.size.join(", "),size:this.humanFileSize(this.settings.maxFileSize)}),this._rejectedFiles.size=[],Craft.cp.displayError(t)),this._rejectedFiles.limit.length&&(t=1===this._rejectedFiles.limit.length?"The file {files} could not be uploaded, because the field limit has been reached.":"The files {files} could not be uploaded, because the field limit has been reached.",t=Craft.t("app",t,{files:this._rejectedFiles.limit.join(", ")}),this._rejectedFiles.limit=[],Craft.cp.displayError(t))},humanFileSize:function(t){var e=1024;if(t=e);return t.toFixed(1)+" "+["kB","MB","GB","TB","PB","EB","ZB","YB"][n]},_createExtensionList:function(){this._extensionList=[];for(var t=0;t=0;--i){var s=this.tryEntries[i],o=s.completion;if("root"===s.tryLoc)return r("end");if(s.tryLoc<=this.prev){var l=a.call(s,"catchLoc"),c=a.call(s,"finallyLoc");if(l&&c){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&a.call(r,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),P(n),b}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;P(n)}return i}}throw Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:M(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=n),b}},r}function n(t,e,n,r,i,a,s){try{var o=t[a](s),l=o.value}catch(t){return void n(t)}o.done?e(l):Promise.resolve(l).then(r,i)}Craft.AssetSelectInput=Craft.BaseElementSelectInput.extend({$uploadBtn:null,uploader:null,progressBar:null,openPreviewTimeout:null,init:function(){this.base.apply(this,arguments),this.settings.canUpload&&this._attachUploader(),this.updateAddElementsBtn(),this.addListener(this.$elementsContainer,"keydown",this._onKeyDown.bind(this))},elementSelectSettings:function(){return Object.assign(this.base(),{makeFocusable:!0})},_onKeyDown:function(t){if(t.keyCode===Garnish.SPACE_KEY&&t.shiftKey)return this.openPreview(),t.stopPropagation(),!1},clearOpenPreviewTimeout:function(){this.openPreviewTimeout&&(clearTimeout(this.openPreviewTimeout),this.openPreviewTimeout=null)},openPreview:function(t){Craft.PreviewFileModal.openInstance?Craft.PreviewFileModal.openInstance.hide():(t||(t=this.$elements.filter(":focus").add(this.$elements.has(":focus"))),t.length&&Craft.PreviewFileModal.showForAsset(t,this.elementSelect))},_attachUploader:function(){var t=this;this.progressBar=new Craft.ProgressBar($('
      ').appendTo(this.$container)),this.$addElementBtn&&(this.$uploadBtn=$("
      ').addClass().appendTo(Garnish.$bod)),this.$prompt=$('
      ').appendTo(this.$modalContainerDiv.empty()),this.$promptMessage=$('

      ').appendTo(this.$prompt),this.$promptChoices=$('

      ').appendTo(this.$prompt),this.$promptApplyToRemainingContainer=$('
      ",{class:"entry-type-override-settings-body"}),$("
      ",{class:"fields",html:t.settingsHtml}).appendTo(n),i=$("
      ",{class:"entry-type-override-settings-footer"}),$("
      ",{class:"flex-grow"}).appendTo(i),a=Craft.ui.createButton({label:Craft.t("app","Close"),spinner:!0}).appendTo(i),Craft.ui.createSubmitButton({class:"secondary",label:Craft.t("app","Apply"),spinner:!0}).appendTo(i),s=n.add(i),(o=new Craft.Slideout(s,{containerElement:"form",containerAttributes:{action:"",method:"post",novalidate:"",class:"entry-type-override-settings"}})).on("open",(function(){Garnish.requestAnimationFrame((function(){o.$container.find(".text:first").focus()}))})),a.on("click",(function(){o.close()})),!t.headHtml){e.next=13;break}return e.next=13,Craft.appendHeadHtml(t.headHtml);case 13:if(!t.bodyHtml){e.next=16;break}return e.next=16,Craft.appendBodyHtml(t.bodyHtml);case 16:return Craft.initUiElements(o.$container),e.abrupt("return",o);case 18:case"end":return e.stop()}}),e)})))()},applySettings:function(t,n,i){return a(r().mark((function a(){var s,o,l,c,u,h,d;return r().wrap((function(r){for(;;)switch(r.prev=r.next){case 0:return s=n.$container.find("button[type=submit]").addClass("loading"),n.$container.find(".field.has-errors").each((function(t,e){var n=$(e);n.removeClass("has-errors"),n.children(".input").removeClass("errors prevalidate"),n.children("ul.errors").remove()})),r.prev=2,r.prev=3,r.next=6,Craft.sendActionRequest("POST","entry-types/apply-override-settings",{data:{id:t.data("id"),settingsNamespace:i,settings:n.$container.serialize()}});case 6:l=r.sent,o=l.data,r.next=16;break;case 10:throw r.prev=10,r.t0=r.catch(3),(h=null===r.t0||void 0===r.t0||null===(c=r.t0.response)||void 0===c||null===(c=c.data)||void 0===c?void 0:c.errors)&&Object.entries(h).forEach((function(t){var r=e(t,2),i=r[0],a=r[1],s=n.$container.find('[data-error-key="'.concat(i,'"]'));s.length&&Craft.ui.addErrorsToField(s,a)})),Craft.cp.displayError(null===r.t0||void 0===r.t0||null===(u=r.t0.response)||void 0===u||null===(u=u.data)||void 0===u?void 0:u.message),r.t0;case 16:d=$(o.chipHtml),t.find(".chip-label").replaceWith(d.find(".chip-label")),t.find("input").val(JSON.stringify(o.config)),n.close(),n.destroy();case 21:return r.prev=21,s.removeClass("loading"),r.finish(21);case 24:case"end":return r.stop()}}),a,null,[[2,,21,24],[3,10]])})))()},renderSettings:function(t){return Object.assign(this.base(),{inputValue:this.settings.allowOverrides?JSON.stringify({id:t}):null})}},{defaults:{allowOverrides:!1}})},4004:function(){Craft.ProgressBar=Garnish.Base.extend({$progressBar:null,$innerProgressBar:null,$progressBarStatus:null,intervalManager:null,_itemCount:0,_progressPercentage:null,_processedItemCount:0,_displaySteps:!1,init:function(t,e,n){var r=this;this.setSettings(n,Craft.ProgressBar.defaults),e&&(this._displaySteps=!0),this.$progressBar=$('