diff --git a/appinfo/info.xml b/appinfo/info.xml index 72004d86d..2be03a6f4 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -14,7 +14,7 @@ - 🚀 Get your project organized - 0.4.0-alpha1 + 0.4.0-alpha3 agpl Julius Härtl Deck diff --git a/css/style.scss b/css/style.scss index 0469ebbb4..fc36e81c6 100644 --- a/css/style.scss +++ b/css/style.scss @@ -461,6 +461,20 @@ input.input-inline { } } + .card-tasks { + border-radius: 3px; + margin: 4px 4px 4px 0px; + padding: 0 2px; + font-size: 90%; + opacity: 0.5; + display: flex; + align-items: center; + + .icon { + background-size: contain; + } + } + button { padding: 22px; margin: 0; @@ -1219,6 +1233,17 @@ input.input-inline { white-space: pre; } } + + input[type=checkbox] { + margin: 0px 10px 0px 0px; + line-height: 10px; + font-size: 10px; + display: inline-block; + min-height: 12px; + } + li input[type=checkbox] { + margin: 0px 10px 0px -20px; + } } /** diff --git a/js/app/Config.js b/js/app/Config.js index 8f3c7fa3d..a4f7acd91 100644 --- a/js/app/Config.js +++ b/js/app/Config.js @@ -25,18 +25,20 @@ import app from './App.js'; import md from 'angular-markdown-it'; import markdownitLinkTarget from 'markdown-it-link-target'; +import markdownitCheckbox from 'legacy/markdown-it-checkbox.js'; app.config(function ($provide, $interpolateProvider, $httpProvider, $urlRouterProvider, $stateProvider, $compileProvider, markdownItConverterProvider) { 'use strict'; $httpProvider.defaults.headers.common.requesttoken = oc_requesttoken; + $compileProvider.debugInfoEnabled(true); markdownItConverterProvider.use(markdownitLinkTarget, { breaks: true, linkify: true, xhtmlOut: true - }); + }).use(markdownitCheckbox); $urlRouterProvider.otherwise('/'); diff --git a/js/controller/BoardController.js b/js/controller/BoardController.js index 2d2d1e374..6d4d7e615 100644 --- a/js/controller/BoardController.js +++ b/js/controller/BoardController.js @@ -49,6 +49,22 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St }, true); $scope.params = $state; + /** + * Check for markdown checkboxes in description to render the counter + * + * This should probably be moved to the backend at some point + * + * @param text + * @returns array of [finished, total] checkboxes + */ + $scope.getCheckboxes = function(text) { + const regTotal = /\[(X|\s|\_|\-)\]\s(.*)/ig; + const regFinished = /\[(X|\_|\-)\]\s(.*)/ig; + return [ + ((text || '').match(regFinished) || []).length, + ((text || '').match(regTotal) || []).length + ]; + }; $scope.search = function (searchText) { $scope.searchText = searchText; diff --git a/js/controller/CardController.js b/js/controller/CardController.js index 49f60d5ca..f35a38817 100644 --- a/js/controller/CardController.js +++ b/js/controller/CardController.js @@ -23,7 +23,7 @@ /* global app moment */ import app from '../app/App.js'; -app.controller('CardController', function ($scope, $rootScope, $location, $stateParams, $interval, $timeout, $filter, BoardService, CardService, StackService, StatusService) { +app.controller('CardController', function ($scope, $rootScope, $sce, $location, $stateParams, $interval, $timeout, $filter, BoardService, CardService, StackService, StatusService, markdownItConverter) { $scope.sidebar = $rootScope.sidebar; $scope.status = { lastEdit: 0, @@ -38,9 +38,19 @@ app.controller('CardController', function ($scope, $rootScope, $location, $state $scope.statusservice.retainWaiting(); + $scope.description = function() { + return $scope.rendered; + }; + + $scope.updateMarkdown = function(content) { + // only trust the html from markdown-it-checkbox + $scope.rendered = $sce.trustAsHtml(markdownItConverter.render(content || '')); + }; + CardService.fetchOne($scope.cardId).then(function (data) { $scope.statusservice.releaseWaiting(); $scope.archived = CardService.getCurrent().archived; + $scope.updateMarkdown(CardService.getCurrent().description); }, function (error) { }); @@ -51,7 +61,44 @@ app.controller('CardController', function ($scope, $rootScope, $location, $state $scope.status.cardRename = true; } }; - $scope.cardEditDescriptionShow = function ($event) { + + $scope.toggleCheckbox = function (id) { + $('#markdown input[type=checkbox]').attr('disabled', true); + $scope.status.edit = angular.copy(CardService.getCurrent()); + var reg = /\[(X|\s|\_|\-)\]\s(.*)/ig; + var nth = 0; + $scope.status.edit.description = $scope.status.edit.description.replace(reg, function (match, i, original) { + if (nth++ === id) { + var result; + if (match.match(/^\[\s\]/i)) { + result = match.replace(/\[\s\]/i, '[x]'); + } + if (match.match(/^\[x\]/i)) { + result = match.replace(/\[x\]/i, '[ ]'); + } + return result; + } + return match; + }); + CardService.update($scope.status.edit).then(function (data) { + var header = $('.section-header.card-description'); + header.find('.save-indicator.unsaved').hide(); + header.find('.save-indicator.saved').fadeIn(250).fadeOut(1000); + StackService.updateCard($scope.status.edit); + }); + $('#markdown input[type=checkbox]').removeAttr('disabled'); + + }; + $scope.clickCardDescription = function ($event) { + var checkboxId = $($event.target).data('id'); + if ($event.target.tagName === 'LABEL') { + $scope.toggleCheckbox(checkboxId); + return; + } + if ($event.target.tagName === 'INPUT') { + $scope.toggleCheckbox(checkboxId); + return; + } if (BoardService.isArchived() || CardService.getCurrent().archived) { return false; } @@ -71,8 +118,9 @@ app.controller('CardController', function ($scope, $rootScope, $location, $state $interval(function() { var currentTime = Date.now(); var timeSinceEdit = currentTime-$scope.status.lastEdit; - if (timeSinceEdit > 1000 && $scope.status.lastEdit > $scope.status.lastSave) { + if (timeSinceEdit > 1000 && $scope.status.lastEdit > $scope.status.lastSave && !$scope.status.saving) { $scope.status.lastSave = currentTime; + $scope.status.saving = true; var header = $('.section-header.card-description'); header.find('.save-indicator.unsaved').fadeIn(500); CardService.update($scope.status.edit).then(function (data) { @@ -80,6 +128,7 @@ app.controller('CardController', function ($scope, $rootScope, $location, $state header.find('.save-indicator.unsaved').hide(); header.find('.save-indicator.saved').fadeIn(250).fadeOut(1000); StackService.updateCard($scope.status.edit); + $scope.status.saving = false; }); } }, 500, 0, false); diff --git a/js/legacy/markdown-it-checkbox.js b/js/legacy/markdown-it-checkbox.js new file mode 100644 index 000000000..1aa1ecf95 --- /dev/null +++ b/js/legacy/markdown-it-checkbox.js @@ -0,0 +1,114 @@ +/** + * Original source code from https://github.com/mcecot/markdown-it-checkbox + * © 2015 Markus Cecot + * licenced under MIT + * https://github.com/mcecot/markdown-it-checkbox/blob/master/LICENSE + */ +var checkboxReplace; + +checkboxReplace = function(md, options, Token) { + "use strict"; + var arrayReplaceAt, createTokens, defaults, lastId, pattern, splitTextToken; + arrayReplaceAt = md.utils.arrayReplaceAt; + lastId = 0; + defaults = { + divWrap: false, + divClass: 'checkbox', + idPrefix: 'checkbox' + }; + options = Object.assign(defaults, options); + pattern = /\[(X|\s|\_|\-)\]\s(.*)/i; + createTokens = function(checked, label, Token) { + var id, idNumeric, nodes, token; + nodes = []; + + /** + *
+ */ + if (options.divWrap) { + token = new Token("checkbox_open", "div", 1); + token.attrs = [["class", options.divClass]]; + nodes.push(token); + } + + /** + * + */ + id = options.idPrefix + lastId; + idNumeric = lastId; + lastId += 1; + token = new Token("checkbox_input", "input", 0); + token.attrs = [["type", "checkbox"], ["id", id], ["data-id", idNumeric]]; + if (checked === true) { + token.attrs.push(["checked", "true"]); + } + nodes.push(token); + + /** + *