diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js index dcbd8b72..b32f9b5d 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js @@ -5,79 +5,72 @@ function InputfieldTextTags($parent) { var $inputs = jQuery('.InputfieldTextTagsInput:not(.selectized)', $parent); var $selects = jQuery('.InputfieldTextTagsSelect:not(.selectized)', $parent); - if($inputs.length) { - $inputs.selectize({ - plugins: ['remove_button', 'drag_drop'], - delimiter: ' ', - persist: false, - createOnBlur: true, - submitOnReturn: false, - create: function(input) { - return { - value: input, - text: input - } + var defaults = { + plugins: [ 'remove_button', 'drag_drop' ], + delimiter: ' ', + persist: true, // If false, items created by the user will not show up as available options once they are unselected. + submitOnReturn: false, + openOnFocus: true, // Show the dropdown immediately when the control receives focus. + closeAfterSelect: true, // If true, the dropdown will be closed after a selection is made. + copyClassesToDropdown: false, + createOnBlur: true, // If true, when user exits the field (clicks outside of input), a new option is created and selected (if create setting is enabled). + selectOnTab: true, // If true, the tab key will choose the currently selected item. + maxItems: null, // The max number of items the user can select. 1 makes the control mono-selection, null allows an unlimited number of items. + create: function(input) { + return { + value: input, + text: input } + } + }; + + if($inputs.length) { + $inputs.each(function() { + $input = $(this); + var o = JSON.parse($input.attr('data-opts')); + var options = defaults; + options.delimiter = o.delimiter; + options.closeAfterSelect = o.closeAfterSelect; + options.persist = false; + $input.selectize(options); }); } if($selects.length) { $selects.each(function() { var $select = $(this); - var configName = $select.attr('data-cfgname'); - var allowUserTags = $select.hasClass('InputfieldTextTagsSelectOnly') ? false : true; - var tags = []; + 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; - if(configName.length) { - tags = ProcessWire.config[configName]; - } else { - tags = $select.attr('data-tags'); - tags = JSON.parse(tags); - } + for(var tag in tags) { var label = tags[tag]; tagsList[n] = { value: tag, label: label }; n++; } - $select.selectize({ - plugins: ['remove_button', 'drag_drop'], - delimiter: ' ', + + var options = jQuery.extend(defaults, { + allowUserTags: o.allowUserTags, + delimiter: o.delimiter, + closeAfterSelect: o.closeAfterSelect, persist: true, - submitOnReturn: false, - closeAfterSelect: true, - copyClassesToDropdown: false, - createOnBlur: true, - maxItems: null, valueField: 'value', labelField: 'label', - searchField: ['value', 'label'], + searchField: [ 'value', 'label' ], options: tagsList, - create: function(input) { - return { - value: input, - text: input - } - }, createFilter: function(input) { - if(allowUserTags) return true; + if(o.allowUserTags) return true; allow = false; for(var n = 0; n < tags.length; n++) { - if(typeof tagsList[input] !== "undefined") { + if(typeof tags[input] !== "undefined") { allow = true; break; } } return allow; }, - /* - onDropdownOpen: function($dropdown) { - $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 100); - }, - onDropdownClose: function($dropdown) { - $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 'auto'); - }, - */ render: { item: function(item, escape) { if(typeof item.label === "undefined" || !item.label.length) item.label = item.value; @@ -88,7 +81,17 @@ function InputfieldTextTags($parent) { return '
' + escape(item.label) + '
'; } } - }); + /* + onDropdownOpen: function($dropdown) { + $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 100); + }, + onDropdownClose: function($dropdown) { + $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 'auto'); + }, + */ + }); + + $select.selectize(options); }); } } diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js index 1ce9e54d..fb8d2546 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=$(".InputfieldForm");var $inputs=jQuery(".InputfieldTextTagsInput:not(.selectized)",$parent);var $selects=jQuery(".InputfieldTextTagsSelect:not(.selectized)",$parent);if($inputs.length){$inputs.selectize({plugins:["remove_button","drag_drop"],delimiter:" ",persist:false,createOnBlur:true,submitOnReturn:false,create:function(input){return{value:input,text:input}}})}if($selects.length){$selects.each(function(){var $select=$(this);var configName=$select.attr("data-cfgname");var allowUserTags=$select.hasClass("InputfieldTextTagsSelectOnly")?false:true;var tags=[];var tagsList=[];var n=0;if(configName.length){tags=ProcessWire.config[configName]}else{tags=$select.attr("data-tags");tags=JSON.parse(tags)}for(var tag in tags){var label=tags[tag];tagsList[n]={value:tag,label:label};n++}$select.selectize({plugins:["remove_button","drag_drop"],delimiter:" ",persist:true,submitOnReturn:false,closeAfterSelect:true,copyClassesToDropdown:false,createOnBlur:true,maxItems:null,valueField:"value",labelField:"label",searchField:["value","label"],options:tagsList,create:function(input){return{value:input,text:input}},createFilter:function(input){if(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)+"
"}}})})}}jQuery(document).ready(function($){InputfieldTextTags();$(document).on("reloaded",".InputfieldTextTags",function(){InputfieldTextTags($(this))})}); \ No newline at end of file +function InputfieldTextTags($parent){if(typeof $parent==="undefined")$parent=$(".InputfieldForm");var $inputs=jQuery(".InputfieldTextTagsInput:not(.selectized)",$parent);var $selects=jQuery(".InputfieldTextTagsSelect:not(.selectized)",$parent);var defaults={plugins:["remove_button","drag_drop"],delimiter:" ",persist:true,submitOnReturn:false,openOnFocus:true,closeAfterSelect:true,copyClassesToDropdown:false,createOnBlur:true,selectOnTab:true,maxItems:null,create:function(input){return{value:input,text:input}}};if($inputs.length){$inputs.each(function(){$input=$(this);var o=JSON.parse($input.attr("data-opts"));var options=defaults;options.delimiter=o.delimiter;options.closeAfterSelect=o.closeAfterSelect;options.persist=false;$input.selectize(options)})}if($selects.length){$selects.each(function(){var $select=$(this);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,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)+"
"}}});$select.selectize(options)})}}jQuery(document).ready(function($){InputfieldTextTags();$(document).on("reloaded",".InputfieldTextTags",function(){InputfieldTextTags($(this))})}); \ No newline at end of file diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.module b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.module index ce587a17..3d38aeaa 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.module +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.module @@ -22,7 +22,9 @@ * ~~~~~ * * @property array|string $tagsList Array of tags [ 'tag' => 'label' ], or newline separated string of "tag=label", or use addTag() to populate. - * @property int|bool $allowUserTags Allow user-entered tags? + * @property int|bool $allowUserTags Allow user-entered tags? + * @property int|bool $closeAfterSelect Close select dropdown box after user makes selection? + * @property string $delimiter One of 's' (for space ' '), 'p' (for pipe '|') or 'c' (for comma). * @property string $value * @property-read array $arrayValue * @@ -51,6 +53,8 @@ class InputfieldTextTags extends Inputfield public function __construct() { $this->set('tagsList', array()); $this->set('allowUserTags', 0); + $this->set('closeAfterSelect', 1); + $this->set('delimiter', 's'); parent::__construct(); } @@ -168,7 +172,7 @@ class InputfieldTextTags extends Inputfield $tagString = trim($tagString); $tagArray = array(); if(!strlen($tagString)) return $tagArray; - $a = explode(' ', $tagString); + $a = explode($this->delimiter(), $tagString); foreach($a as $key => $tag) { $tag = trim("$tag"); if(!strlen($tag)) continue; @@ -188,7 +192,7 @@ class InputfieldTextTags extends Inputfield * */ public function tagArrayToString(array $tagArray) { - return trim(implode(' ', $tagArray)); + return trim(implode($this->delimiter(), $tagArray)); } /** @@ -497,20 +501,25 @@ class InputfieldTextTags extends Inputfield $config = $this->wire()->config; $class = $this->className(); + + $opts = array( + 'allowUserTags' => $this->allowUserTags, + 'closeAfterSelect' => $this->closeAfterSelect, + 'delimiter' => $this->delimiter(), + ); if($this->hasField) { // page editor - $name = $class . '_' . $this->hasField->name . '__tags'; - $data = $config->$name ? $config->$name : array(); + $cfgName = $class . '_' . $this->hasField->name . '__tags'; + $data = $config->$cfgName ? $config->$cfgName : array(); $data = array_unique(array_merge($data, $tags)); - $config->js($name, $data); - $attrs['data-cfgname'] = $name; + $config->js($cfgName, $data); + $opts['cfgName'] = $cfgName; } else { // other usages - $attrs['data-cfgname'] = ''; - $attrs['data-tags'] = json_encode($tags, JSON_UNESCAPED_UNICODE); + $opts['tags'] = $tags; } - + $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'; @@ -565,7 +574,7 @@ class InputfieldTextTags extends Inputfield foreach($tags as $key => $tag) { if(ctype_digit("$tag")) $tags[$key] = "_$tag"; } - return $getArray ? $tags : implode(' ', $tags); + return $getArray ? $tags : implode($this->delimiter(), $tags); } /** @@ -590,7 +599,7 @@ class InputfieldTextTags extends Inputfield } } } - return trim(implode(' ', $tags)); + return trim(implode($this->delimiter(), $tags)); } /** @@ -639,7 +648,7 @@ class InputfieldTextTags extends Inputfield * ~~~~~ * $field = $fields->get('tags'); // tags field using FieldtypeText * $tags = $page->get('tags'); // page value (string of tags, i.e. "foo bar baz") - * $labels = InputfieldTextTags::tagsLabels($field, $tags); + * $labels = InputfieldTextTags::tagsArray($field, $tags); * foreach($labels as $tag => $label) { * echo "
  • $tag: $label
  • "; * } @@ -652,7 +661,7 @@ class InputfieldTextTags extends Inputfield * @return array * */ - public static function tagsLabels(Field $field, $tags = null) { + public static function tagsArray(Field $field, $tags = null) { if(is_string($tags) && !strlen($tags)) return array(); /** @var InputfieldTextTags $inputfield */ $inputfield = $field->wire()->modules->getModule('InputfieldTextTags', array('noInit' => true)); @@ -689,6 +698,21 @@ class InputfieldTextTags extends Inputfield return $labels; } + /** + * Get or set delimiter + * + * @param bool $getName + * @return string + * + */ + protected function delimiter($getName = false) { + $ds = array('s' => ' ', 'c' => ',', 'p' => '|'); + $d = $this->delimiter; + if($getName) return $d; + if(isset($ds[$d])) return $ds[$d]; + if(in_array($d, $ds)) return $d; + return ' '; + } /** * Config @@ -717,19 +741,12 @@ class InputfieldTextTags extends Inputfield return $inputfields; } - /** @var InputfieldToggle $f */ - $f = $modules->get('InputfieldToggle'); - $f->attr('name', 'allowUserTags'); - $f->label = $this->_('Allow user to enter their own tags?'); - $f->val($this->allowUserTags); - $fieldset->add($f); - /** @var InputfieldTextarea $f */ $f = $modules->get('InputfieldTextarea'); $f->attr('name', 'tagsList'); $f->label = $this->label = $this->_('Predefined tags'); $f->description = $this->_('Enter predefined tags, 1 per line. To define separate tag and label, specify `tag=label` on the line.'); - $f->notes = $this->_('Tags may not contain whitespace but labels can.'); + $f->notes = $this->_('Tags may not contain the delimiter selected below but labels can.'); $f->val($this->tagsListArrayToString($this->tagsList)); if($languages) { $f->description .= ' ' . $this->_('To define separate labels per-language, re-enter each tag (with label) for each language.'); @@ -740,6 +757,34 @@ class InputfieldTextTags extends Inputfield } $fieldset->add($f); + /** @var InputfieldToggle $f */ + $f = $modules->get('InputfieldToggle'); + $f->attr('name', 'allowUserTags'); + $f->label = $this->_('Allow user to enter their own tags?'); + $f->val($this->allowUserTags); + $f->columnWidth = 33; + $fieldset->add($f); + + /** @var InputfieldToggle $f */ + $f = $modules->get('InputfieldToggle'); + $f->attr('name', 'closeAfterSelect'); + $f->label = $this->_('Close dropdown after each selection is made'); + $f->columnWidth = 34; + $f->val($this->closeAfterSelect); + $fieldset->add($f); + + /** @var InputfieldRadios $f */ + $f = $modules->get('InputfieldRadios'); + $f->attr('name', 'delimiter'); + $f->label = $this->_('Tag delimiter'); + $f->addOption('s', $this->_('Space')); + $f->addOption('c', $this->_('Comma')); + $f->addOption('p', $this->_('Pipe')); + $f->optionColumns = 1; + $f->columnWidth = 33; + $f->val($this->delimiter(true)); + $fieldset->add($f); + return $inputfields; } diff --git a/wire/modules/Process/ProcessTemplate/ProcessTemplate.module b/wire/modules/Process/ProcessTemplate/ProcessTemplate.module index b74ed107..53e2cef5 100644 --- a/wire/modules/Process/ProcessTemplate/ProcessTemplate.module +++ b/wire/modules/Process/ProcessTemplate/ProcessTemplate.module @@ -23,7 +23,7 @@ * */ -class ProcessTemplate extends Process { +class ProcessTemplate extends Process implements ConfigurableModule { /** @var InputfieldForm */ protected $form; @@ -179,6 +179,9 @@ class ProcessTemplate extends Process { $caseTags = array(); // indexed by lowercase version of tag $collapsedTags = $this->wire()->modules->getConfig($this, 'collapsedTags'); if(!is_array($collapsedTags)) $collapsedTags = array(); + foreach($collapsedTags as $key => $tag) { + $collapsedTags[$key] = strtolower($tag); + } if(!$hasFilters) foreach($this->templates as $template) { if($showSystem && ($template->flags & Template::flagSystem)) { @@ -205,7 +208,6 @@ class ProcessTemplate extends Process { if(!isset($caseTags[$tag])) $caseTags[$tag] = $caseTag; } } - $tagCnt = count($templatesByTag); if($tagCnt > 1) { /** @var InputfieldWrapper $form */ @@ -239,6 +241,13 @@ class ProcessTemplate extends Process { $button->addClass('add_template_button'); $button->showInHeader(); $out .= $button->render(); + + $button = $this->modules->get('InputfieldButton'); + $button->id = 'tags_button'; + $button->href = './tags/'; + $button->icon = 'tags'; + $button->value = $this->labels['manageTags']; + $out .= $button->render(); $button = $this->modules->get('InputfieldButton'); $button->id = 'import_button'; @@ -3509,6 +3518,14 @@ class ProcessTemplate extends Process { return $result; } + + /** + * Build a form allowing configuration of this Module + * + * @param InputfieldWrapper $inputfields + * + */ + public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { } }