From 674b44b2882cded8e4ad4a2a5d66abdbf230b7ee Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Thu, 12 Aug 2021 09:31:28 -0400 Subject: [PATCH] Fix issue processwire/processwire-issues#1421 - InputfieldTextTags not working in repeater items --- .../InputfieldTextTags/InputfieldTextTags.js | 6 +- .../InputfieldTextTags.min.js | 2 +- .../InputfieldTextTags.module | 101 +++++++++++++----- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js index 1fc7b7c4..b8dd9fd4 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js @@ -64,7 +64,7 @@ function InputfieldTextTags($parent) { options: tagsList, createFilter: function(input) { if(o.allowUserTags) return true; - allow = false; + var allow = false; for(var n = 0; n < tags.length; n++) { if(typeof tags[input] !== "undefined") { allow = true; @@ -147,5 +147,7 @@ function InputfieldTextTags($parent) { jQuery(document).ready(function($) { InputfieldTextTags(); - $(document).on('reloaded', '.InputfieldTextTags', function() { InputfieldTextTags($(this)); }); + $(document).on('reloaded', '.InputfieldTextTags', function() { + InputfieldTextTags($(this)); + }); }); \ No newline at end of file diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js index 78ad4052..2bde0b6d 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js @@ -1 +1 @@ -function InputfieldTextTags($parent){if(typeof $parent==="undefined")$parent=jQuery(".InputfieldForm");var pluginsMulti=["remove_button","drag_drop"];var pluginsSingle=[];var defaults={delimiter:" ",persist:true,submitOnReturn:false,openOnFocus:true,closeAfterSelect:true,copyClassesToDropdown:false,createOnBlur:false,selectOnTab:true,maxItems:null,create:function(input){return{value:input,text:input}}};function initInput($input){var o=JSON.parse($input.attr("data-opts"));var options=defaults;options.delimiter=o.delimiter;options.closeAfterSelect=o.closeAfterSelect;options.createOnBlur=o.createOnBlur;options.persist=false;options.maxItems=o.maxItems>0?o.maxItems:null;options.plugins=o.maxItems===1?pluginsSingle:pluginsMulti;$input.selectize(options)}function initSelect($select){var o=JSON.parse($select.attr("data-opts"));var cfgName=typeof o.cfgName==="undefined"?"":o.cfgName;var tags=cfgName.length?ProcessWire.config[cfgName]:o.tags;var tagsList=[];var n=0;for(var tag in tags){var label=tags[tag];tagsList[n]={value:tag,label:label};n++}var options=jQuery.extend(defaults,{allowUserTags:o.allowUserTags,delimiter:o.delimiter,closeAfterSelect:o.closeAfterSelect,createOnBlur:o.createOnBlur,maxItems:o.maxItems>0?o.maxItems:null,plugins:o.maxItems===1?pluginsSingle:pluginsMulti,persist:true,valueField:"value",labelField:"label",searchField:["value","label"],options:tagsList,createFilter:function(input){if(o.allowUserTags)return true;allow=false;for(var n=0;n"+escape(item.label)+""},option:function(item,escape){if(typeof item.label==="undefined"||!item.label.length)item.label=item.value;return"
"+escape(item.label)+"
"}}});if(o.tagsUrl.length){options.load=function(query,callback){if(!query.length)return callback();var tagsUrl=o.tagsUrl.replace("{q}",encodeURIComponent(query));Inputfields.startSpinner($select);jQuery.ajax({url:tagsUrl,type:"GET",error:function(){Inputfields.stopSpinner($select);callback()},success:function(items){for(var n=0;n0?o.maxItems:null;options.plugins=o.maxItems===1?pluginsSingle:pluginsMulti;$input.selectize(options)}function initSelect($select){var o=JSON.parse($select.attr("data-opts"));var cfgName=typeof o.cfgName==="undefined"?"":o.cfgName;var tags=cfgName.length?ProcessWire.config[cfgName]:o.tags;var tagsList=[];var n=0;for(var tag in tags){var label=tags[tag];tagsList[n]={value:tag,label:label};n++}var options=jQuery.extend(defaults,{allowUserTags:o.allowUserTags,delimiter:o.delimiter,closeAfterSelect:o.closeAfterSelect,createOnBlur:o.createOnBlur,maxItems:o.maxItems>0?o.maxItems:null,plugins:o.maxItems===1?pluginsSingle:pluginsMulti,persist:true,valueField:"value",labelField:"label",searchField:["value","label"],options:tagsList,createFilter:function(input){if(o.allowUserTags)return true;var allow=false;for(var n=0;n"+escape(item.label)+""},option:function(item,escape){if(typeof item.label==="undefined"||!item.label.length)item.label=item.value;return"
"+escape(item.label)+"
"}}});if(o.tagsUrl.length){options.load=function(query,callback){if(!query.length)return callback();var tagsUrl=o.tagsUrl.replace("{q}",encodeURIComponent(query));Inputfields.startSpinner($select);jQuery.ajax({url:tagsUrl,type:"GET",error:function(){Inputfields.stopSpinner($select);callback()},success:function(items){for(var n=0;n __('Text Tags', __FILE__), // Module Title 'summary' => __('Enables input of user entered tags or selection of predefined tags.', __FILE__), // Module Summary - 'version' => 4, + 'version' => 5, 'icon' => 'tags', ); } + /** + * Cache for getRenderReadyTags() method + * + * @var array + * + */ + protected $renderReadyTags = array(); + /** * Tags set in 'value' attribute that were not in predefined list * @@ -498,6 +506,17 @@ class InputfieldTextTags extends Inputfield implements $jQueryUI = $this->wire()->modules->get('JqueryUI'); $jQueryUI->use('selectize'); $this->addClass('InputfieldNoFocus', 'wrapClass'); + $tags = $this->getRenderReadyTags(); + + if($this->hasField) { + // page editor: populate selectable tags to ProcessWire.config JS + $config = $this->wire()->config; + $cfgName = $this->getJsCfgName(); + $data = $config->$cfgName ? $config->$cfgName : array(); + $data = array_unique(array_merge($data, $tags)); + $config->js($cfgName, $data); + } + return parent::renderReady($parent, $renderValueMode); } @@ -527,30 +546,12 @@ class InputfieldTextTags extends Inputfield implements $attrs = $this->getAttributes(); unset($attrs['class']); - $language = $this->wire()->user->language; - $tags = $this->getTagsList($language && $language->id ? $language : null); - $classes = array(); - $classes[] = count($tags) || $tagsUrl ? 'InputfieldTextTagsSelect' : 'InputfieldTextTagsInput'; - - if($this->allowUserTags) { - $value = $this->tagStringToArray($this->val()); - foreach($value as $tag) { - if(!isset($tags[$tag])) $tags[$tag] = $tag; - } - } else { - $classes[] = 'InputfieldTextTagsSelectOnly'; - } + $tags = $this->getRenderReadyTags(); + $this->renderReadyTags = array(); // reset cache + $classes = (count($tags) || $tagsUrl ? array('InputfieldTextTagsSelect') : array('InputfieldTextTagsInput')); + + if(!$this->allowUserTags) $classes[] = 'InputfieldTextTagsSelectOnly'; - $a = $tags; - $tags = array(); - foreach($a as $tag => $label) { - // ensure no digit-only tags which do not survive json_encode() - if(ctype_digit("$tag")) $tag = "_$tag"; - $tags[$tag] = $label; - } - - $class = $this->className(); - $opts = array( 'allowUserTags' => $this->allowUserTags(), 'closeAfterSelect' => $this->closeAfterSelect, @@ -562,11 +563,7 @@ class InputfieldTextTags extends Inputfield implements if($this->hasField) { // page editor - $cfgName = $class . '_' . $this->hasField->name . '__tags'; - $data = $config->$cfgName ? $config->$cfgName : array(); - $data = array_unique(array_merge($data, $tags)); - $config->js($cfgName, $data); - $opts['cfgName'] = $cfgName; + $opts['cfgName'] = $this->getJsCfgName(); } else { // other usages $opts['tags'] = $tags; @@ -575,13 +572,59 @@ class InputfieldTextTags extends Inputfield implements $attrs['data-opts'] = json_encode($opts, JSON_UNESCAPED_UNICODE); $attrs['class'] = trim(implode(' ', $classes)); $attrs['value'] = $this->encodeNumericTags($this->val()); + if(empty($attrs['type'])) $attrs['type'] = 'text'; + $attrStr = $this->getAttributesString($attrs); $out = ""; return $out; } + + /** + * Get JS config property name used in ProcessWire.config[propertyName] + * + * @return string + * + */ + protected function getJsCfgName() { + return ($this->hasField ? $this->className() . '_' . $this->hasField->name . '__tags' : ''); + } + + /** + * Get tags ready for use by renderReady() or render() + * + * @return array|string + * @throws WireException + * + */ + protected function getRenderReadyTags() { + + if(count($this->renderReadyTags)) return $this->renderReadyTags; + + $language = $this->wire()->user->language; + $tags = $this->getTagsList($language && $language->id ? $language : null); + + if($this->allowUserTags) { + $value = $this->tagStringToArray($this->val()); + foreach($value as $tag) { + if(!isset($tags[$tag])) $tags[$tag] = $tag; + } + } + + $a = $tags; + $tags = array(); + foreach($a as $tag => $label) { + // ensure no digit-only tags which do not survive json_encode() + if(ctype_digit("$tag")) $tag = "_$tag"; + $tags[$tag] = $label; + } + + $this->renderReadyTags = $tags; + + return $tags; + } /** * Render value