Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
feat(chips): Make chips editable
Browse files Browse the repository at this point in the history
Closes #7579
  • Loading branch information
mertdeg2 authored and ThomasBurleson committed Mar 15, 2016
1 parent a653122 commit c3085ee
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 10 deletions.
5 changes: 5 additions & 0 deletions src/components/chips/chips-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ md-chips.md-THEME_NAME-theme {
color: '{{primary-contrast}}';
}
}

&._md-chip-editing {
background: transparent;
color: '{{background-800}}';
}
}
md-chip-remove {
.md-button {
Expand Down
6 changes: 6 additions & 0 deletions src/components/chips/chips.scss
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ $contact-chip-name-width: rem(12) !default;
outline: none;
}
}
&._md-chip-content-edit-is-enabled {
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
-moz-user-select: none; /* mozilla browsers */
-khtml-user-select: none; /* webkit (konqueror) browsers */
-ms-user-select: none; /* IE10+ */
}
._md-chip-remove-container {
position: absolute;
@include rtl-prop(right, left, 0);
Expand Down
6 changes: 6 additions & 0 deletions src/components/chips/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ <h2 class="md-title">Use the default chip template.</h2>

<md-chips ng-model="ctrl.fruitNames" readonly="ctrl.readonly"></md-chips>


<br/>
<h2 class="md-title">Make chips editable.</h2>

<md-chips ng-model="ctrl.editableFruitNames" readonly="ctrl.readonly" md-enable-chip-edit="true"></md-chips>

<br/>
<h2 class="md-title">Use Placeholders and override hint texts.</h2>

Expand Down
2 changes: 2 additions & 0 deletions src/components/chips/demoBasicUsage/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// Lists of fruit names and Vegetable objects
self.fruitNames = ['Apple', 'Banana', 'Orange'];
self.roFruitNames = angular.copy(self.fruitNames);
self.editableFruitNames = angular.copy(self.fruitNames);

self.tags = [];
self.vegObjs = [
{
Expand Down
192 changes: 192 additions & 0 deletions src/components/chips/js/chipController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
angular
.module('material.components.chips')
.controller('MdChipCtrl', MdChipCtrl);

/**
* Controller for the MdChip component. Responsible for handling keyboard
* events and editting the chip if needed.
*
* @param $scope
* @param $element
* @param $mdConstant
* @param $timeout
* @param $mdUtil
* @constructor
*/
function MdChipCtrl ($scope, $element, $mdConstant, $timeout, $mdUtil) {
/**
* @type {$scope}
*/
this.$scope = $scope;

/**
* @type {$element}
*/
this.$element = $element;

/**
* @type {$mdConstant}
*/
this.$mdConstant = $mdConstant;

/**
* @type {$timeout}
*/
this.$timeout = $timeout;

/**
* @type {$mdUtil}
*/
this.$mdUtil = $mdUtil;

/**
* @type {boolean}
*/
this.isEditting = false;

/**
* @type {MdChipsCtrl}
*/
this.parentController = undefined;

/**
* @type {boolean}
*/
this.enableChipEdit = false;
}


/**
* @param {MdChipsCtrl} controller
*/
MdChipCtrl.prototype.init = function(controller) {
this.parentController = controller;
this.enableChipEdit = this.parentController.enableChipEdit;

if (this.enableChipEdit) {
this.$element.on('keydown', this.chipKeyDown.bind(this));
this.$element.on('mousedown', this.chipMouseDown.bind(this));
this.getChipContent().addClass('_md-chip-content-edit-is-enabled');
}
};


/**
* @return {Object}
*/
MdChipCtrl.prototype.getChipContent = function() {
var chipContents = this.$element[0].getElementsByClassName('_md-chip-content');
return angular.element(chipContents[0]);
};


/**
* @return {Object}
*/
MdChipCtrl.prototype.getContentElement = function() {
return angular.element(this.getChipContent().children()[0]);
};


/**
* @return {number}
*/
MdChipCtrl.prototype.getChipIndex = function() {
return parseInt(this.$element.attr('index'));
};


/**
* Presents an input element to edit the contents of the chip.
*/
MdChipCtrl.prototype.goOutOfEditMode = function() {
if (!this.isEditting) return;

this.isEditting = false;
this.$element.removeClass('_md-chip-editing');
this.getChipContent()[0].contentEditable = 'false';
var chipIndex = this.getChipIndex();

var content = this.getContentElement().text();
if (content) {
this.parentController.updateChipContents(
chipIndex,
this.getContentElement().text()
);

this.$mdUtil.nextTick(function() {
if (this.parentController.selectedChip === chipIndex) {
this.parentController.focusChip(chipIndex);
}
}.bind(this));
} else {
this.parentController.removeChipAndFocusInput(chipIndex);
}
};


/**
* Given an HTML element. Selects contents of it.
* @param node
*/
MdChipCtrl.prototype.selectNodeContents = function(node) {
var range, selection;
if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(node);
range.select();
} else if (window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
}
};


/**
* Presents an input element to edit the contents of the chip.
*/
MdChipCtrl.prototype.goInEditMode = function() {
this.isEditting = true;
this.$element.addClass('_md-chip-editing');
this.getChipContent()[0].contentEditable = 'true';
this.getChipContent().on('blur', function() {
this.goOutOfEditMode();
}.bind(this));

this.selectNodeContents(this.getChipContent()[0]);
};


/**
* Handles the keydown event on the chip element. If enable-chip-edit attribute is
* set to true, space or enter keys can trigger going into edit mode. Enter can also
* trigger submitting if the chip is already being edited.
* @param event
*/
MdChipCtrl.prototype.chipKeyDown = function(event) {
if (!this.isEditting &&
(event.keyCode === this.$mdConstant.KEY_CODE.ENTER ||
event.keyCode === this.$mdConstant.KEY_CODE.SPACE)) {
event.preventDefault();
this.goInEditMode();
} else if (this.isEditting &&
event.keyCode === this.$mdConstant.KEY_CODE.ENTER) {
event.preventDefault();
this.goOutOfEditMode();
}
};


/**
* Handles the double click event
*/
MdChipCtrl.prototype.chipMouseDown = function() {
if(this.getChipIndex() == this.parentController.selectedChip &&
this.enableChipEdit &&
!this.isEditting) {
this.goInEditMode();
}
};
25 changes: 16 additions & 9 deletions src/components/chips/js/chipDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,37 @@ var DELETE_HINT_TEMPLATE = '\
* MDChip Directive Definition
*
* @param $mdTheming
* @param $mdInkRipple
* @param $mdUtil
* @ngInject
*/
function MdChip($mdTheming, $mdUtil) {
var hintTemplate = $mdUtil.processTemplate(DELETE_HINT_TEMPLATE);

return {
restrict: 'E',
require: '^?mdChips',
compile: compile
require: ['^?mdChips', 'mdChip'],
compile: compile,
controller: 'MdChipCtrl'
};

function compile(element, attr) {
// Append the delete template
element.append($mdUtil.processTemplate(hintTemplate));

return function postLink(scope, element, attr, ctrl) {
return function postLink(scope, element, attr, ctrls) {
var chipsController = ctrls.shift();
var chipController = ctrls.shift();
$mdTheming(element);

if (ctrl) angular.element(element[0].querySelector('._md-chip-content'))
.on('blur', function () {
ctrl.resetSelectedChip();
ctrl.$scope.$applyAsync();
});
if (chipsController) {
chipController.init(chipsController);

angular.element(element[0].querySelector('._md-chip-content'))
.on('blur', function () {
chipsController.selectedChip = -1;
chipsController.$scope.$applyAsync();
});
}
};
}
}
30 changes: 29 additions & 1 deletion src/components/chips/js/chipsController.js
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ angular
* @param $mdConstant
* @param $log
* @param $element
* @param $mdUtil
* @constructor
*/
function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout) {
function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout, $mdUtil) {
/** @type {$timeout} **/
this.$timeout = $timeout;

Expand Down Expand Up @@ -50,6 +51,8 @@ function MdChipsCtrl ($scope, $mdConstant, $log, $element, $timeout) {
/** @type {boolean} */
this.hasAutocomplete = false;

/** @type {string} */
this.enableChipEdit = $mdUtil.parseAttributeBoolean(this.mdEnableChipEdit);

/**
* Hidden hint text for how to delete a chip. Used to give context to screen readers.
Expand Down Expand Up @@ -146,13 +149,38 @@ MdChipsCtrl.prototype.inputKeydown = function(event) {
}
};


/**
* Updates the content of the chip at given index
* @param chipIndex
* @param chipContents
*/
MdChipsCtrl.prototype.updateChipContents = function(chipIndex, chipContents){
if(chipIndex >= 0 && chipIndex < this.items.length) {
this.items[chipIndex] = chipContents;
this.ngModelCtrl.$setDirty();
}
};


/**
* Returns true if a chip is currently being edited. False otherwise.
* @return {boolean}
*/
MdChipsCtrl.prototype.isEditingChip = function(){
return !!this.$element[0].getElementsByClassName('_md-chip-editing').length;
};


/**
* Handles the keydown event on the chip elements: backspace removes the selected chip, arrow
* keys switch which chips is active
* @param event
*/
MdChipsCtrl.prototype.chipKeydown = function (event) {
if (this.getChipBuffer()) return;
if (this.isEditingChip()) return;

switch (event.keyCode) {
case this.$mdConstant.KEY_CODE.BACKSPACE:
case this.$mdConstant.KEY_CODE.DELETE:
Expand Down
4 changes: 4 additions & 0 deletions src/components/chips/js/chipsDirective.js
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
* @param {boolean=} readonly Disables list manipulation (deleting or adding list items), hiding
* the input and delete buttons. If no `ng-model` is provided, the chips will automatically be
* marked as readonly.
* @param {string=} md-enable-chip-edit Set this to "true" to enable editing of chip contents. The user can
* go into edit mode with pressing "space", "enter", or double clicking on the chip. Chip edit is only
* supported for chips with basic template.
* @param {number=} md-max-chips The maximum number of chips allowed to add through user input.
* <br/><br/>The validation property `md-max-chips` can be used when the max chips
* amount is reached.
Expand Down Expand Up @@ -195,6 +198,7 @@
scope: {
readonly: '=readonly',
placeholder: '@',
mdEnableChipEdit: '@',
secondaryPlaceholder: '@',
maxChips: '@mdMaxChips',
transformChip: '&mdTransformChip',
Expand Down

0 comments on commit c3085ee

Please sign in to comment.