From f3088b5b48daf558a9005083f177760fac02c722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Sat, 29 Apr 2017 11:41:43 +0200 Subject: [PATCH 1/6] Add support for checkboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- css/style.scss | 11 +++++++ js/app/Config.js | 4 ++- js/controller/CardController.js | 53 +++++++++++++++++++++++++++++++-- templates/part.card.php | 9 +++--- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/css/style.scss b/css/style.scss index 1f6f77fbd..d5248aff3 100644 --- a/css/style.scss +++ b/css/style.scss @@ -1197,6 +1197,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/CardController.js b/js/controller/CardController.js index 49f60d5ca..3f7bf5f47 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,42 @@ 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 +116,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 +126,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/templates/part.card.php b/templates/part.card.php index 2880c782e..a0684b638 100644 --- a/templates/part.card.php +++ b/templates/part.card.php @@ -98,14 +98,13 @@ placeholder="t('Add a card description…')); ?>" ng-blur="cardUpdate(status.edit)" ng-model="status.edit.description" - ng-change="cardEditDescriptionChanged()" + ng-change="cardEditDescriptionChanged(); updateMarkdown(status.edit.description)" autofocus-on-insert> -
-
+
{{ description() }}
t('Add a card description…')); ?>
+ ng-if="!description()">t('Add a card description…')); ?>
From 20490dbd399d16e14dc774eb444d813e58542818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Tue, 15 May 2018 21:28:19 +0200 Subject: [PATCH 2/6] Add indicator for tasks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- css/style.scss | 14 ++++++++++++++ js/controller/BoardController.js | 16 ++++++++++++++++ templates/part.board.mainView.php | 4 ++++ 3 files changed, 34 insertions(+) diff --git a/css/style.scss b/css/style.scss index d5248aff3..8180a4ac6 100644 --- a/css/style.scss +++ b/css/style.scss @@ -457,6 +457,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; 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/templates/part.board.mainView.php b/templates/part.board.mainView.php index 1de9a543b..05dff539f 100644 --- a/templates/part.board.mainView.php +++ b/templates/part.board.mainView.php @@ -80,6 +80,10 @@ {{ c.duedate | relativeDateFilterString }} +
+ + {{ getCheckboxes(c.description)[0] }}/{{ getCheckboxes(c.description)[1] }} +
From 1a4b2e5f014adedbc96a75787eff3750e251436f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Tue, 15 May 2018 23:11:40 +0200 Subject: [PATCH 3/6] Add fork of markdown-it-checkboxes to legacy scripts folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- js/legacy/markdown-it-checkbox.js | 110 ++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 js/legacy/markdown-it-checkbox.js diff --git a/js/legacy/markdown-it-checkbox.js b/js/legacy/markdown-it-checkbox.js new file mode 100644 index 000000000..3f195748d --- /dev/null +++ b/js/legacy/markdown-it-checkbox.js @@ -0,0 +1,110 @@ +var _, checkboxReplace; + +_ = require('underscore'); + +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 = _.extend(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); + + /** + *