Commit cc24859b authored by Randall Leeds's avatar Randall Leeds

Greatly simplify the tags

Take control over the CSS and simplify the directives.
- Drop jquery.tagit.css. It's easy enough to style ourselves and most
  of it comes from the ui theme anyway.
- Simplify the directive.
  - Don't swap between editor and viewer widgets. Manage the disabled
    state, placeholder, and styling differences ourselves.
  - Stop creating a new scope. This simplifies the model binding by
    avoiding the $parent eyesore used in the annotation. This was
    accomplished by observing the readonly attribute instead of watching
    a scope variable.
  - Use the <ul> form of tagit rather than the input. The need for a
    template is completely removed.
- Allow underscores in tag names.
parent e2b148f2
......@@ -1030,55 +1030,47 @@ pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
// Tags for the annotations
.tags {
@include pie-clearfix;
@include reset-box-model;
@include reset-font;
background: none;
clear: both;
margin-top: .2em;
margin-bottom: .5em;
// Add a border to the new tag entry field
.wl-editor .tagit-new input[type=text] {
border: 1px solid gray;
padding: .2em;
.tagit-close {
cursor: pointer;
position: absolute;
right: .1em;
top: 50%;
margin-top: -8px;
line-height: 17px;
}
// We don't want no new input in read only mode
.wl-displayer .tagit-new {
display: none;
}
.text-icon { display: none; }
// Remove border and padding from the tag editor widget.
// Since both the tags and the new tag entry field have their own borders,
// this was redundant.
.wl-editor ul, .wl-displayer ul {
border: none;
padding: 0;
}
input { float: left; }
// Use our usual fonts for the tag labels and input
.ui-widget, .tagit-label, .tagit-new input {
font: inherit;
font-size: 100%;
}
// Add some small space around tag displayer
.wl-displayer {
margin-top: .2em;
margin-bottom: .5em;
}
li {
display: block;
float: left;
margin: .1em;
padding: .1em 1.2em .1em .3em;
position: relative;
// We don't need that much padding
ul.tagit li.ui-corner-all {
padding: .1em .3em;
& + li { margin-left: .1em; }
}
// .. except for the x-mark when editing.
.wl-editor ul.tagit li.ui-corner-all {
padding-right: 1.2em;
&[readonly] {
input, .tagit-close { display: none; }
li { padding-right: .3em; }
}
}
// Only show one line of tags in bucket view
.summary .tags {
.wl-displayer ul {
height: 2.0em;
overflow: hidden;
}
height: 2.0em;
overflow: hidden;
}
// Tags for the displayer page
......@@ -1104,30 +1096,6 @@ pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
}
// Tags in replies for the displayer page
.displayer-reply-tags {
// We don't want no new input here
.tagit-new {
display: none;
}
// Main widget needs no border or bg
.tagit {
border: none;
}
// Use our usual fonts for the tag labels
.ui-widget, .tagit-label, {
font: inherit;
font-size: 100%;
}
// We don't need that much padding
ul.tagit li.ui-corner-all {
padding: .1em .3em;
}
}
// Special rules for tags on user stream page
// (To be applied on top of the rules for the displayer page
.userstream-tags .displayer-tags {
......
......@@ -263,88 +263,44 @@ repeatAnim = ->
.animate({ 'margin-left': '0px' }, 1500)
# Directive to edit/display a word list. Used for tags.
wordlist = ['$filter', '$timeout', '$window', ($filter, $timeout, $window) ->
tags = ['$window', ($window) ->
link: (scope, elem, attr, ctrl) ->
return unless ctrl?
input = elem.find('.wl-editor input')
output = elem.find('.wl-displayer input')
# Updates a tag-it widget with the requested viewValue
update_widget = (widget) ->
# Check whether the current content of the editor
# is in sync with the actual model value
current = widget.assignedTags()
wanted = ctrl.$viewValue or []
if (current + '') is (wanted + '')
# We are good to go, nothing to do
else
# Editor widget's content is different.
# (Probably because of a cancelled edit.)
# Copy the tags to the tag editor
widget.removeAll()
for tag in wanted
widget.createTag tag
widgets = {}
# Re-render the word list when the view needs updating.
ctrl.$render = ->
words = (ctrl.$viewValue or [])
if words[0] is "" then words = []
scope.words = words
if scope.readonly
if widgets.displayer?
# update the displayer widget
update_widget widgets.displayer
else
# Create displayer widget
output.val (words.join ",")
output.tagit
readOnly: true
onTagClicked: (evt, ui) ->
tag = ui.tagLabel
$window.open "/t/" + tag
widgets.displayer = output.data "uiTagit"
elem.tagit
caseSensitive: false
placeholderText: attr.placeholder
keepPlaceholder: true
preprocessTag: (val) ->
val.toLowerCase().replace /[^a-z0-9\-\_\s]/g, ''
afterTagAdded: (evt, ui) ->
ctrl.$setViewValue elem.tagit 'assignedTags'
afterTagRemoved: (evt, ui) ->
ctrl.$setViewValue elem.tagit 'assignedTags'
autocomplete:
source: []
onTagClicked: (evt, ui) ->
tag = ui.tagLabel
$window.open "/t/" + tag
ctrl.$formatters.push (tags=[]) ->
assigned = elem.tagit 'assignedTags'
for t in assigned when t not in tags
elem.tagit 'removeTagByLabel', t
for t in tags when t not in assigned
elem.tagit 'createTag', t
attr.$observe 'readonly', (readonly) ->
tagInput = elem.find('input').last()
if readonly
tagInput.attr('disabled', true)
tagInput.removeAttr('placeholder')
else
if widgets.editor?
# Update the editor widget
update_widget widgets.editor
else
# Create editor widget
input.val (words.join ",")
input.tagit
caseSensitive: false
placeholderText: scope.placeholder
keepPlaceholder: true
afterTagAdded: (evt, ui) ->
if ui.duringInitialization then return
newTab = ui.tagLabel
# Create a normalized form
normalized = newTab.toLowerCase().replace /[^a-z0-9\-\s]/g, ''
if newTab is normalized
tagsChanged()
else
widgets.editor.removeTagByLabel newTab, false
widgets.editor.createTag normalized
afterTagRemoved: tagsChanged
autocomplete:
source: []
widgets.editor = input.data "uiTagit"
# React to the changes in the tag editor
tagsChanged = -> ctrl.$setViewValue input.val().split ","
# Re-render when it becomes editable / uneditable.
scope.$watch 'readonly', (readonly) -> ctrl.$render()
tagInput.removeAttr('disabled')
tagInput.attr('placeholder', attr['placeholder'])
require: '?ngModel'
restrict: 'E'
scope:
readonly: '@'
placeholder: '@'
template: '<div ng-hide="readonly" class="wl-editor"><input /></div><div ng-show="readonly && words" class="wl-displayer"><input /></div>'
restrict: 'C'
]
angular.module('h.directives', ['ngSanitize'])
......@@ -355,9 +311,9 @@ angular.module('h.directives', ['ngSanitize'])
.directive('resettable', resettable)
.directive('slowValidate', slowValidate)
.directive('tabReveal', tabReveal)
.directive('tags', tags)
.directive('thread', thread)
.directive('userPicker', userPicker)
.directive('ngBlur', ngBlur)
.directive('repeatAnim', repeatAnim)
.directive('wordlist', wordlist)
ul.tagit {
padding: 1px 5px;
overflow: auto;
margin-left: inherit; /* usually we don't want the regular ul margins. */
margin-right: inherit;
}
ul.tagit li {
display: block;
float: left;
margin: 2px 5px 2px 0;
}
ul.tagit li.tagit-choice {
position: relative;
line-height: inherit;
}
ul.tagit li.tagit-choice-read-only {
padding: .2em .5em .2em .5em;
}
ul.tagit li.tagit-choice-editable {
padding: .2em 18px .2em .5em;
}
ul.tagit li.tagit-new {
padding: .25em 4px .25em 0;
}
ul.tagit li.tagit-choice a.tagit-label {
cursor: pointer;
text-decoration: none;
}
ul.tagit li.tagit-choice .tagit-close {
cursor: pointer;
position: absolute;
right: .1em;
top: 50%;
margin-top: -8px;
line-height: 17px;
}
/* used for some custom themes that don't need image icons */
ul.tagit li.tagit-choice .tagit-close .text-icon {
display: none;
}
ul.tagit li.tagit-choice input {
display: block;
float: left;
margin: 2px 5px 2px 0;
}
ul.tagit input[type="text"] {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
border: none;
margin: 0;
padding: 0;
width: inherit;
background-color: inherit;
outline: none;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment