├── PREVIEW.gif ├── package.json ├── assets ├── css │ └── list.css └── js │ └── list.js ├── template.php ├── README.md ├── LICENSE.txt └── list.php /PREVIEW.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOetting/kirby-list-field/HEAD/PREVIEW.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "list", 3 | "description": "Kirby List Field", 4 | "author": "Tim Ötting", 5 | "version": "1.0", 6 | "type": "kirby-field", 7 | "license": "MIT" 8 | } 9 | -------------------------------------------------------------------------------- /assets/css/list.css: -------------------------------------------------------------------------------- 1 | .list-field .list-item{ 2 | margin-bottom: .5em; 3 | position: relative; 4 | } 5 | 6 | .list-field .list-item .sortable-handle{ 7 | pointer-events: auto; 8 | cursor: move; 9 | } -------------------------------------------------------------------------------- /template.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 10 | 11 |
12 | value() as $listItemValue): ?> 13 |
14 | inputField($listItemValue); ?> 15 |
16 |
17 | 18 |
19 | 20 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # List Field for Kirby CMS 2 | 3 | This is a custom list field for [Kirby](http://getkirby.com). It lets you write and sort a simple list of text inputs. 4 | 5 | Here is a blueprint example: 6 | 7 | fields: 8 | ... 9 | fruits: 10 | label: Give Me Fruits 11 | type: list 12 | placeholder: Add a new fruit 13 | 14 | Have a look at the resulting panel field: 15 | 16 | ![list field demo](https://github.com/TimOetting/kirby-list-field/blob/master/PREVIEW.gif?raw=true) 17 | 18 | The content will be YAML-structured. Inside the template, the field therefore has to be decoded as an array using $page->fruits()->yaml(). 19 | 20 | ---- 21 | 22 | Fruits: 23 | 24 | - Lemons 25 | - Apples 26 | - Banana 27 | - Oranges 28 | 29 | ## Setup 30 | Using git, go to the root folder of your Kirby project and run ``git clone https://github.com/TimOetting/kirby-list-field.git site/fields/list``. You can also just copy the content of this repository into ``site/fields/list``. 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tim Ötting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /list.php: -------------------------------------------------------------------------------- 1 | array( 11 | 'list.js' 12 | ), 13 | 'css' => array( 14 | 'list.css' 15 | ) 16 | ); 17 | 18 | public function inputField($value) { 19 | 20 | $input = new Brick('input', null); 21 | $input->addClass('input'); 22 | $input->attr(array( 23 | 'type' => $this->type(), 24 | 'value' => $value, 25 | 'required' => $this->required(), 26 | 'name' => $this->name() . '[]', 27 | 'autocomplete' => $this->autocomplete() === false ? 'off' : 'on', 28 | 'autofocus' => $this->autofocus(), 29 | 'readonly' => $this->readonly(), 30 | 'disabled' => $this->disabled(), 31 | 'id' => $this->id() 32 | )); 33 | 34 | if(!is_array($value)) { 35 | $input->val($value, false); 36 | } 37 | 38 | if($this->readonly()) { 39 | $input->attr('tabindex', '-1'); 40 | $input->addClass('input-is-readonly'); 41 | } 42 | 43 | return $input; 44 | 45 | } 46 | 47 | public function item($value, $text) { 48 | 49 | $input = $this->input($value); 50 | 51 | $label = new Brick('input', $this->i18n($text)); 52 | $label->addClass('input'); 53 | $label->attr('data-focus', 'true'); 54 | $label->prepend($input); 55 | 56 | if($this->readonly) { 57 | $label->addClass('input-is-readonly'); 58 | } 59 | 60 | return $label; 61 | 62 | } 63 | 64 | public function content() { 65 | 66 | return tpl::load(__DIR__ . DS . 'template.php', array('field' => $this)); 67 | 68 | } 69 | 70 | public function result() { 71 | 72 | $result = parent::result(); 73 | return yaml::encode($result); 74 | 75 | } 76 | 77 | public function value() { 78 | 79 | $value = parent::value(); 80 | return yaml::decode($value); 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /assets/js/list.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var List = function(element) { 3 | 4 | $(element).on('input', '.add-input', function(e){ 5 | var list = $(element).find('.list'); 6 | var newListItemHtml = $(element).find('.list-item-template').html(); 7 | list.append(newListItemHtml); 8 | var newListItem = list.find('.list-item').last(); 9 | var newInput = newListItem.find('input'); 10 | newInput.val($(e.target).val()) 11 | $(e.target).val('') 12 | console.log(newInput.focus()); 13 | }) 14 | 15 | $(element).on('keydown', '.list .input', function(e){ 16 | var listItem = $(e.target).parents('.list-item'); 17 | var closestInput = (listItem.prev().length > 0) ? listItem.prev().find('input') : listItem.next().find('input') 18 | if(closestInput.length == 0){ 19 | closestInput = $(element).find('.add-input'); 20 | } 21 | if($(e.target).val() == '' && e.keyCode == 8){ //Backspace 22 | e.preventDefault(); 23 | listItem.remove(); 24 | setCursorToEnd(closestInput) 25 | } 26 | if(e.keyCode == 13){ //Enter 27 | e.preventDefault() 28 | $(element).find('.add-input').focus() 29 | } 30 | }) 31 | 32 | $(element).on('focus', 'input', function(e){ 33 | var list = $(element).find('.list'); 34 | var listInputs = list.find('input'); 35 | $.each(listInputs, function(i, input){ 36 | if($(input).val() == ''){ 37 | $(input).parent('.list-item').remove(); 38 | } 39 | }) 40 | }) 41 | 42 | $(element).find('.list').sortable({ 43 | handle: '.sortable-handle' 44 | }); 45 | 46 | }; 47 | 48 | var setCursorToEnd = function(element){ 49 | 50 | var value = element.val(); 51 | element.focus(); 52 | element.val('') 53 | element.val(value) 54 | 55 | } 56 | 57 | $.fn.list = function() { 58 | 59 | return this.each(function() { 60 | 61 | if($(this).data('list')) { 62 | return $(this).data('list'); 63 | } else { 64 | var list = new List(this); 65 | $(this).data('list', list); 66 | return list; 67 | } 68 | 69 | }); 70 | 71 | }; 72 | 73 | })(jQuery); --------------------------------------------------------------------------------