UNPKG

kdf

Version:

KD: a non-document focused UI Framework for web applications.

192 lines (150 loc) 5.5 kB
KDContentEditableView = require './contenteditableview.coffee' KDContextMenu = require './../contextmenu/contextmenu.coffee' module.exports = class KDTokenizedInput extends KDContentEditableView constructor: (options = {}, data) -> options.cssClass = KD.utils.curry 'kdtokenizedinputview', options.cssClass options.bind = KD.utils.curry 'keyup', options.bind options.rules or= {} options.layer or= {} super options, data @tokenViews = {} getValue: (options = {}) -> value = "" for node in @getEditableElement().childNodes value += "\n" if node.tagName?.toLowerCase() is "div" nodeValue = @getValueOfNode node value += nodeValue if nodeValue isnt "\n" if value is Encoder.htmlDecode(@getOptions().placeholder) then return "" else return value getValueOfNode: (node) -> value = "" switch node.nodeType when Node.TEXT_NODE value += node.textContent if node.textContent isnt "" when Node.ELEMENT_NODE value += @getValueOfElement node return value getValueOfElement: (element) -> key = element.dataset?.key value = @getValueOfTokenElement key if key return value if value tagName = element.tagName.toLowerCase() switch tagName when "br" then return "\n" else value = "" value += @getValueOfNode child for child in element.childNodes return value or "" getValueOfTokenElement: (key) -> view = @getTokenView key return view.encodeValue() if key and view getTokens: -> return @findTokensInElement @getEditableElement() findTokensInElement: (element) -> tokens = [] for child in element.childNodes switch child.nodeType when Node.ELEMENT_NODE if key = child.dataset?.key view = @getTokenView key {type} = view.getOptions() data = view.getData() tokens.push {type, data} else tokens = tokens.concat @findTokensInElement child return tokens getTokenView: (key) -> return @tokenViews[key] matchPrefix: -> return if @tokenInput return unless range = @utils.getSelectionRange() node = range.commonAncestorContainer if node.children?.length is 1 return node.textContent is node.children[0].textContent start = range.startOffset - 1 char = node.textContent[start] for name, rule of @getOptions().rules if char is rule.prefix @activeRule = rule @tokenInput = document.createElement "span" @tokenInput.textContent = rule.prefix @utils.replaceRange node, @tokenInput, start, start + rule.prefix.length @utils.selectText @tokenInput, rule.prefix.length matchToken: -> return @cancel() unless @tokenInput.parentNode @sanitizeInput() token = @tokenInput.textContent.substring @activeRule.prefix.length return @cancel() if /\s/.test token return if token is @lastToken @lastToken = token if token.trim() {dataSource} = @activeRule dataSource token, @bound 'showMenu' else if token.length isnt 0 @cancel() sanitizeInput: -> showMenu:(options = {}, data)-> @menu?.destroy() @menu = null return unless @tokenInput and data.length pos = @tokenInput.getBoundingClientRect() options.x ?= pos.left options.y ?= pos.top + parseInt window.getComputedStyle(@tokenInput).lineHeight, 10 @menu = new KDContextMenu options, data @menu.on 'ContextMenuItemReceivedClick', @bound 'menuItemClicked' hideMenu: -> @menu?.destroy() @menu = null @activeRule = null @tokenInput = null menuItemClicked: (item, tokenViewClass) -> @addToken item.data, tokenViewClass @hideMenu() addToken: (item, tokenViewClass = @getOptions().tokenViewClass) -> {type, prefix, pistachio} = @activeRule tokenView = new tokenViewClass {type, prefix, pistachio}, item tokenElement = tokenView.getElement() tokenKey = "#{tokenView.getId()}-#{tokenView.getKey()}" @tokenViews[tokenKey] = tokenView tokenView.setAttributes "data-key": tokenKey @tokenInput.parentElement.insertBefore tokenElement, @tokenInput tokenView.emit "viewAppended" @tokenInput.nextSibling.textContent = "\u00a0" @utils.selectText @tokenInput.nextSibling, 1 @tokenInput.remove() @emit "TokenAdded", type, item keyDown: (event) -> switch event.which when 9, 13, 27, 38, 40 # enter, tab, escape, up, down if @menu @menu.treeController.keyEventHappened event KD.utils.stopDOMEvent event else super event switch event.which when 27 then @cancel() if @tokenInput keyUp: (event) -> super switch event.which when 9, 13, 27, 38, 40 # enter, tab, escape, up, down else return if event.altKey or event.ctrlKey or event.metaKey if @activeRule then @matchToken() else @matchPrefix() cancel: -> if @tokenInput.parentNode text = document.createTextNode @tokenInput.textContent @tokenInput.parentElement.insertBefore text, @tokenInput @tokenInput.nextSibling.remove() @tokenInput.remove() @utils.selectEnd text @hideMenu() reset: -> @setPlaceholder() @blur() for own id, view of @tokenViews view.destroy() delete @tokenViews[id] viewAppended: -> super @setEditingMode on