@stackbit/datocms-plugin-typed-list
Advanced tools
Sorry, the diff of this file is not supported yet
+26
-18
| { | ||
| "name": "@stackbit/datocms-plugin-typed-list", | ||
| "homepage": "https://github.com/stackbithq/datocms-plugin-typed-list#readme", | ||
| "version": "0.1.3", | ||
| "version": "0.1.4", | ||
| "description": "Plugin for editing typed list (list of strings, enums, numbers) in DatoCMS using JSON field", | ||
@@ -10,3 +10,6 @@ "main": "index.js", | ||
| }, | ||
| "files": ["public", "docs"], | ||
| "files": [ | ||
| "public", | ||
| "docs" | ||
| ], | ||
| "datoCmsPlugin": { | ||
@@ -16,21 +19,26 @@ "title": "Typed List Editor", | ||
| "entryPoint": "public/index.html", | ||
| "fieldTypes": ["json"], | ||
| "fieldTypes": [ | ||
| "json" | ||
| ], | ||
| "pluginType": "field_editor", | ||
| "parameters": { | ||
| "global": [], | ||
| "instance": [{ | ||
| "id": "type", | ||
| "label": "Type", | ||
| "hint": "The type of list items, possible values: 'string' or 'number'", | ||
| "type": "string", | ||
| "required": false, | ||
| "default": "string" | ||
| }, { | ||
| "id": "options", | ||
| "label": "Options", | ||
| "hint": "List of comma separated allowed values. Used when type is set to 'string'", | ||
| "type": "string", | ||
| "required": false, | ||
| "default": "" | ||
| }] | ||
| "instance": [ | ||
| { | ||
| "id": "type", | ||
| "label": "Type", | ||
| "hint": "The type of list items, possible values: 'string' or 'number'", | ||
| "type": "string", | ||
| "required": false, | ||
| "default": "string" | ||
| }, | ||
| { | ||
| "id": "options", | ||
| "label": "Options", | ||
| "hint": "List of comma separated allowed values. Used when type is set to 'string'", | ||
| "type": "string", | ||
| "required": false, | ||
| "default": "" | ||
| } | ||
| ] | ||
| } | ||
@@ -37,0 +45,0 @@ }, |
+41
-2
@@ -16,3 +16,3 @@ ul { | ||
| display: flex; | ||
| height: 32px; | ||
| min-height: 36px; | ||
| align-items: center; | ||
@@ -27,5 +27,28 @@ } | ||
| flex: 1 1 auto; | ||
| padding: 0 8px; | ||
| padding: 10px 0; | ||
| cursor: pointer; | ||
| } | ||
| ul#list li .edit-controls { | ||
| flex: 1 1 auto; | ||
| position: relative; | ||
| } | ||
| ul#list li .edit-input { | ||
| width: 100%; | ||
| border: 0; | ||
| font-size: 16px; | ||
| padding-right: 40px; | ||
| box-sizing: border-box; | ||
| } | ||
| ul#list li .edit-button { | ||
| position: absolute; | ||
| right: 10px; | ||
| top: 50%; | ||
| transform: translateY(-50%); | ||
| cursor: pointer; | ||
| color: #777; | ||
| } | ||
| ul#list li .remove-button { | ||
@@ -38,2 +61,3 @@ display: inline-block; | ||
| text-align: center; | ||
| padding-right: 5px; | ||
| } | ||
@@ -43,4 +67,19 @@ | ||
| vertical-align: bottom; | ||
| color: #777; | ||
| } | ||
| ul#list li .drag-handle { | ||
| padding: 0 16px; | ||
| cursor: move; | ||
| color: #777; | ||
| } | ||
| ul#list li.moving { | ||
| background-color: #c8effb; | ||
| } | ||
| ul#list.is-dragging { | ||
| cursor: move; | ||
| } | ||
| .inputLabel { | ||
@@ -47,0 +86,0 @@ color: #34363a; |
+168
-26
@@ -9,3 +9,3 @@ function deserializeValue(value) { | ||
| function getFieldValue(plugin) { | ||
| const fieldValue = plugin.getFieldValue(plugin.fieldPath); | ||
| var fieldValue = plugin.getFieldValue(plugin.fieldPath); | ||
| return deserializeValue(fieldValue); | ||
@@ -19,3 +19,3 @@ } | ||
| function getFieldType(plugin) { | ||
| let type = _.get(plugin, 'parameters.instance.type', 'string'); | ||
| var type = _.get(plugin, 'parameters.instance.type', 'string'); | ||
| if (!_.includes(['string', 'number'], type)) { | ||
@@ -29,6 +29,6 @@ console.error('illegal type "' + type + '" for datocms-plugin-typed-list'); | ||
| function getOptions(plugin) { | ||
| let type = getFieldType(plugin); | ||
| var type = getFieldType(plugin); | ||
| if (type === 'string') { | ||
| let options = _.get(plugin, 'parameters.instance.options', ''); | ||
| let optionsArr = _.chain(options).split(',').map(option => _.trim(option)).compact().value(); | ||
| var options = _.get(plugin, 'parameters.instance.options', ''); | ||
| var optionsArr = _.chain(options).split(',').map(option => _.trim(option)).compact().value(); | ||
| return _.isEmpty(optionsArr) ? null : optionsArr; | ||
@@ -44,3 +44,3 @@ } else { | ||
| _.forEach([''].concat(options), option => { | ||
| let optionElm = document.createElement('option'); | ||
| var optionElm = document.createElement('option'); | ||
| optionElm.name = option; | ||
@@ -57,3 +57,3 @@ optionElm.appendChild(document.createTextNode(option)); | ||
| _.forEach(list, item => { | ||
| let liElm = createListItem(item, listElm, list, plugin); | ||
| var liElm = createListItem(item, listElm, list, plugin); | ||
| listElm.appendChild(liElm); | ||
@@ -68,14 +68,74 @@ }); | ||
| function createListEditControls(liElm, span, value, plugin) { | ||
| var editControls = document.createElement('div'); | ||
| var editInput = document.createElement('input'); | ||
| var editButton = document.createElement('span'); | ||
| var editButtonIcon = document.createElement('i'); | ||
| editControls.style.display = 'none'; | ||
| editControls.className = 'edit-controls'; | ||
| editButtonIcon.className = 'fas fa-check'; | ||
| editInput.className = 'edit-input'; | ||
| editButton.className = 'edit-button'; | ||
| editControls.appendChild(editInput); | ||
| editControls.appendChild(editButton); | ||
| editButton.appendChild(editButtonIcon); | ||
| span.addEventListener('click', function() { | ||
| editInput.style.height = span.parentElement.clientHeight + 'px'; | ||
| span.style.display = 'none'; | ||
| editControls.style.display = 'block'; | ||
| editInput.value = value; | ||
| editInput.focus(); | ||
| }); | ||
| var commitEdit = function() { | ||
| var value = editInput.value.trim(); | ||
| if (value) { | ||
| var list = getFieldValue(plugin); | ||
| editItem(liElm, liElm.parentElement, list, value, plugin); | ||
| } | ||
| editControls.style.display = 'none'; | ||
| span.style.display = 'block'; | ||
| }; | ||
| editButton.addEventListener('click', commitEdit); | ||
| editInput.addEventListener("keyup", function(event) { | ||
| if ((event.keyCode === 13 || event.key === 'Enter')) { | ||
| commitEdit(); | ||
| } | ||
| if ((event.keyCode === 27 || event.key === 'Escape')) { | ||
| editControls.style.display = 'none'; | ||
| span.style.display = 'block'; | ||
| } | ||
| }); | ||
| return editControls; | ||
| } | ||
| function createListItem(value, listElm, list, plugin) { | ||
| let liElm = document.createElement('li'); | ||
| let buttonElm = document.createElement('span'); | ||
| let removeIcon = document.createElement('i'); | ||
| let span = document.createElement('span'); | ||
| let textNode = document.createTextNode(value); | ||
| var liElm = document.createElement('li'); | ||
| var buttonElm = document.createElement('span'); | ||
| var removeIcon = document.createElement('i'); | ||
| var dragContainer = document.createElement('span'); | ||
| var dragIcon = document.createElement('i'); | ||
| var span = document.createElement('span'); | ||
| var textNode = document.createTextNode(value); | ||
| var editControls = createListEditControls(liElm, span, value, plugin); | ||
| span.className = 'item-value'; | ||
| buttonElm.className = 'remove-button'; | ||
| removeIcon.className = 'fas fa-times fa-lg'; | ||
| removeIcon.className = 'fas fa-times'; | ||
| dragContainer.className = 'drag-handle'; | ||
| dragIcon.className = 'fas fa-grip-lines'; | ||
| dragContainer.appendChild(dragIcon); | ||
| buttonElm.appendChild(removeIcon); | ||
| span.appendChild(textNode); | ||
| liElm.setAttribute('draggable', 'true'); | ||
| liElm.appendChild(dragContainer); | ||
| liElm.appendChild(span); | ||
| liElm.append(editControls); | ||
| liElm.appendChild(buttonElm); | ||
@@ -91,3 +151,3 @@ | ||
| function addItem(inputElm, list, listElm, plugin, type, options) { | ||
| let value = inputElm.value; | ||
| var value = inputElm.value; | ||
| if (value !== '') { | ||
@@ -101,3 +161,3 @@ if (type === 'string') { | ||
| } else if (type === 'number') { | ||
| let number = _.toNumber(value); | ||
| var number = _.toNumber(value); | ||
| if (_.isNumber(number)) { | ||
@@ -113,3 +173,3 @@ inputElm.value = ''; | ||
| function removeItem(liElm, listElm, list, plugin) { | ||
| let index = _.indexOf(listElm.childNodes, liElm); | ||
| var index = _.indexOf(listElm.childNodes, liElm); | ||
| _.pullAt(list, [index]); | ||
@@ -119,2 +179,80 @@ setFieldValue(plugin, list); | ||
| function editItem(liElm, listElm, list, value, plugin) { | ||
| var index = _.indexOf(listElm.childNodes, liElm); | ||
| list[index] = value; | ||
| setFieldValue(plugin, list); | ||
| } | ||
| function parentElement(el, sel) { | ||
| do { | ||
| if (el.matches(sel)) { | ||
| return el; | ||
| } | ||
| } while (el = el.parentElement); | ||
| return null; | ||
| } | ||
| function initReorder(listElm, plugin) { | ||
| listElm.addEventListener('dragstart', handleDrag); | ||
| listElm.addEventListener('dragover', handleDrag); | ||
| listElm.addEventListener('dragenter', handleDrag); | ||
| listElm.addEventListener('dragend', handleDrag); | ||
| var currentDraggedEl = null; | ||
| var currentDraggedElIndex = null; | ||
| function handleDrag(e) { | ||
| var target = parentElement(e.target, 'li'); | ||
| switch (e.type) { | ||
| case 'dragstart': | ||
| currentDraggedEl = target; | ||
| currentDraggedEl.parentElement.classList.add('is-dragging'); | ||
| currentDraggedElIndex = _.indexOf(currentDraggedEl.parentElement.childNodes, currentDraggedEl); | ||
| e.dataTransfer.effectAllowed = 'move'; | ||
| e.dataTransfer.setData('text/plain', currentDraggedEl.textContent); | ||
| setTimeout(function() { | ||
| currentDraggedEl.classList.add('moving'); | ||
| }); | ||
| break; | ||
| case 'dragover': | ||
| if (e.preventDefault) { | ||
| e.preventDefault(); | ||
| } | ||
| e.dataTransfer.dropEffect = 'move'; | ||
| return false; | ||
| case 'dragenter': | ||
| if (!currentDraggedEl) { | ||
| return; | ||
| } | ||
| if (target && target !== currentDraggedEl) { | ||
| var children = [].slice.call(target.parentElement.children); | ||
| var isToBottom = children.indexOf(currentDraggedEl) < children.indexOf(target); | ||
| currentDraggedEl.parentElement.removeChild(currentDraggedEl); | ||
| target.parentElement.insertBefore(currentDraggedEl, isToBottom ? target.nextSibling : (target.previousSibling || target)); | ||
| } | ||
| break; | ||
| case 'dragend': | ||
| currentDraggedEl.classList.remove('moving'); | ||
| currentDraggedEl.parentElement.classList.remove('is-dragging'); | ||
| var list = getFieldValue(plugin); | ||
| var newIndex = _.indexOf(currentDraggedEl.parentElement.childNodes, currentDraggedEl); | ||
| var el = list[currentDraggedElIndex]; | ||
| list.splice(currentDraggedElIndex, 1); | ||
| list.splice(newIndex, 0, el); | ||
| setFieldValue(plugin, list); | ||
| currentDraggedEl = null; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| function init(plugin) { | ||
@@ -124,10 +262,10 @@ | ||
| let type = getFieldType(plugin); | ||
| let options = getOptions(plugin); | ||
| let list = getFieldValue(plugin); | ||
| let inputElm = document.getElementById('itemInput'); | ||
| let selectElm = document.getElementById('optionSelect'); | ||
| let selectWrapperElm = document.getElementById('selectWrapper'); | ||
| let listElm = document.getElementById('list'); | ||
| let addItemButtonElm = document.getElementById('addItemButton'); | ||
| var type = getFieldType(plugin); | ||
| var options = getOptions(plugin); | ||
| var list = getFieldValue(plugin); | ||
| var inputElm = document.getElementById('itemInput'); | ||
| var selectElm = document.getElementById('optionSelect'); | ||
| var selectWrapperElm = document.getElementById('selectWrapper'); | ||
| var listElm = document.getElementById('list'); | ||
| var addItemButtonElm = document.getElementById('addItemButton'); | ||
@@ -137,3 +275,5 @@ if (!_.isEmpty(options)) { | ||
| } | ||
| resetList(list, listElm, plugin); | ||
| initReorder(listElm, plugin); | ||
@@ -150,3 +290,3 @@ if (type === 'number') { | ||
| addItemButtonElm.addEventListener('click', function() { | ||
| let formElement = _.isEmpty(options) ? inputElm : selectElm; | ||
| var formElement = _.isEmpty(options) ? inputElm : selectElm; | ||
| addItem(formElement, list, listElm, plugin, type, options); | ||
@@ -165,2 +305,3 @@ }); | ||
| } else { | ||
| var list = JSON.stringify(["foo", "bar"]); | ||
| init({ | ||
@@ -172,4 +313,5 @@ callbacks: {}, | ||
| }, | ||
| getFieldValue: () => JSON.stringify(["foo", "bar"]), | ||
| getFieldValue: () => list, | ||
| setFieldValue: function(fieldPath, value) { | ||
| list = value; | ||
| _.invoke(this.callbacks, fieldPath, [value]); | ||
@@ -176,0 +318,0 @@ }, |
+0
-1
@@ -19,4 +19,3 @@ ## Typed List field editor plugin for DatoCMS | ||
| TODOs: | ||
| - rearange items | ||
| - convert to React | ||
| - add more types (color, url, etc.) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
9
12.5%374
66.96%224270
-25.7%21
-4.55%