Merge branch 'apps' of https://github.com/urbit/urbit into work

This commit is contained in:
Philip C Monk 2015-08-20 18:57:43 -04:00
commit df6ddaa493
6 changed files with 302 additions and 94 deletions

View File

@ -280,6 +280,9 @@ h1 {
.input:focus {
background-color: #e6e6e6;
}
.invalid .input {
color: #f00;
}
.caret.left {
border-left: 6px solid #000;
border-top: 6px solid transparent;

View File

@ -203,6 +203,9 @@ h1
.input:focus
background-color #e6e6e6
.invalid .input
color red
.caret.left
border-left 6px solid #000
border-top 6px solid transparent

View File

@ -21,6 +21,11 @@ module.exports =
Persistence.put "new":item
Dispatcher.handleViewAction {type:'newItem', index, item}
changeItem: (id,key,val) ->
set = {}
set[key] = val
Persistence.put old:{id,dif:{set}}
setFilter: (key,val) ->
Dispatcher.handleViewAction
type:'setFilter'

View File

@ -31,10 +31,74 @@ module.exports = recl
e.preventDefault()
return
getVal: ($el,key) ->
if $el[0].tagName is 'TEXTAREA'
return $el.val()
else
if key is 'date-due'
d = $el.text().slice(1).replace(/\./g, "-")
return NaN if d.length < 8
return new Date(d).valueOf()
if key is 'tags'
return $el.text().trim().split(" ")
if key is 'audience'
a = $el.text().trim().split(" ")
a = a.map (_a) -> "~#{_a}"
return a
return $el.text()
compareVal: (l,n,key) ->
if key is 'tags' or key is 'audience'
return (_.xor(l,n).length > 0)
if key is 'date-due'
return l isnt new Date(n)
l isnt n
validateField: ($t,id,key,val) ->
valid = 1
if key is 'date-due'
valid = 0 if isNaN(val)
if key is 'audience'
i = _.filter val,(a) ->
if a[0] isnt "~"
return 0
if a.split("/").length < 2
return 0
if a.split("/")[0].length < 3 or
a.split("/")[1].length < 3
return 0
1
valid = 0 if i.length isnt val.length
valid
_keyUp: (e) ->
$t = $(e.target).closest '.field'
id = $t.closest('.item').attr 'data-id'
key = $t.attr 'data-key'
val = @getVal $t.find('.input'),key
if @compareVal @props.item[key],val,key
if not @validateField($t,id,key,val)
$t.addClass 'invalid'
return
$t.removeClass 'invalid'
if @to then clearTimeout @to
@to = setTimeout ->
WorkActions.changeItem id,key,val
,1000
_focus: (e) -> @props._focus e,@
_markDone: (e) ->
id = $(e.target).closest('.item').attr 'data-id'
WorkActions.changeItem id,'done',true
formatDate: (d) ->
"#{d.getDate()}-#{(d.getMonth()+1)}-#{d.getFullYear()}"
return "" if d is null
"~#{d.getFullYear()}.#{(d.getMonth()+1)}.#{d.getDate()}"
formatAudience: (a) ->
a.join(" ").replace /\~/g,""
getInitialState: -> {expand:false}
@ -45,32 +109,56 @@ module.exports = recl
(div {
className:itemClass
draggable:true
'data-id':@props.item.id
'data-index':@props.index
onDragStart:@_dragStart
onDragEnd:@_dragEnd
'data-index':@props.index
}, [
(div {className:'audience'},@props.item.audience.join(" "))
(div {
className:'audience field'
'data-key':'audience'
},[
(div {
contentEditable:true
className:'input ib'
onKeyUp:@_keyUp
},@formatAudience(@props.item.audience))
])
(div {className:'sort ib top'},@props.item.sort)
(div {className:'done ib'},'')
(div {className:'title ib top'},[
(div {
className:'done ib'
onClick:@_markDone
},'')
(div {
className:'title ib top field'
'data-key':'title'
},[
(div {
contentEditable:true
onFocus:@_focus
onKeyDown:@_keyDown
onKeyUp:@_keyUp
className:'input ib'
},@props.item.title)
])
(div {className:'date ib top'}, [
(div {
className:'date ib top field'
'data-key':'date-due'
}, [
(div {
contentEditable:true
className:'input ib'
},@formatDate(@props.item['date-created']))
onKeyUp:@_keyUp
},@formatDate(@props.item['date-due']))
])
(div {className:'tags ib top'},[
(div {
className:'tags ib top field'
'data-key':'tags'
},[
(div {
contentEditable:true
className:'input ib'
onKeyUp:@_keyUp
},@props.item.tags.join(" "))
])
(div {
@ -80,9 +168,13 @@ module.exports = recl
},[
(div {className:'caret left'},"")
])
(div {className:"description"},[
(div {
className:'description field'
'data-key':'description'
},[
(textarea {
className:'input ib'
onKeyUp:@_keyUp
},@props.item.description)
])
(div {className:"hr"},"")

View File

@ -34,6 +34,19 @@ module.exports = {
item: item
});
},
changeItem: function(id, key, val) {
var set;
set = {};
set[key] = val;
return Persistence.put({
old: {
id: id,
dif: {
set: set
}
}
});
},
setFilter: function(key, val) {
return Dispatcher.handleViewAction({
type: 'setFilter',
@ -81,7 +94,6 @@ module.exports = {
};
},{"../dispatcher/Dispatcher.coffee":8,"../persistence/Persistence.coffee":14}],2:[function(require,module,exports){
var div, h1, label, rece, recl, ref;
@ -109,66 +121,61 @@ module.exports = recl({
key = $t.attr('data-key');
if (txt.length === 0) {
txt = null;
}
if (key === 'audience') {
txt = txt.split(" ");
}
if (key === 'tags') {
txt = [txt];
} else {
switch (key) {
case 'audience':
txt = txt.split(" ");
break;
case 'tags':
txt = [txt];
}
}
return this.props.onChange(key, txt);
},
fields: [
{
filter: 'owned',
key: 'owner',
title: 'Owned by:'
}, {
filter: 'tag',
key: 'tags',
title: 'Tag:'
}, {
filter: 'channel',
key: 'audience',
title: 'Audience:'
}, {
filter: 'status',
key: 'status',
title: 'Status:'
}
],
render: function() {
return div({
className: 'filters'
}, [
div({
className: 'owned filter ib',
'data-key': 'owner'
}, [
label({}, 'Owned by:'), div({
contentEditable: true,
className: 'input ib',
onKeyDown: this._onKeyDown,
onBlur: this._onBlur
}, this.props.filters.owned)
]), div({
className: 'tag filter ib',
'data-key': 'tags'
}, [
label({}, 'Tag:'), div({
contentEditable: true,
className: 'input ib',
onKeyDown: this._onKeyDown,
onBlur: this._onBlur
}, this.props.filters.tag)
]), div({
className: 'channel filter ib',
'data-key': 'audience'
}, [
label({}, 'Audience:'), div({
contentEditable: true,
className: 'input ib',
onKeyDown: this._onKeyDown,
onBlur: this._onBlur
}, this.props.filters.channel)
]), div({
className: 'status filter ib',
'data-key': 'status'
}, [
label({}, 'Status:'), div({
contentEditable: true,
className: 'input ib',
onKeyDown: this._onKeyDown,
onBlur: this._onBlur
}, this.props.filters.status)
])
]);
}, this.fields.map((function(_this) {
return function(arg) {
var filter, key, title;
filter = arg.filter, key = arg.key, title = arg.title;
return div({
key: key,
'data-key': key,
className: filter + " filter ib"
}, [
label({}, title), div({
contentEditable: true,
className: 'input ib',
onKeyDown: _this._onKeyDown,
onBlur: _this._onBlur
}, _this.props.filters[filter])
]);
};
})(this)));
}
});
},{}],3:[function(require,module,exports){
var WorkActions, div, recl, ref, textarea;
@ -211,11 +218,103 @@ module.exports = recl({
e.preventDefault();
}
},
getVal: function($el, key) {
var a, d;
if ($el[0].tagName === 'TEXTAREA') {
return $el.val();
} else {
if (key === 'date-due') {
d = $el.text().slice(1).replace(/\./g, "-");
if (d.length < 8) {
return NaN;
}
return new Date(d).valueOf();
}
if (key === 'tags') {
return $el.text().trim().split(" ");
}
if (key === 'audience') {
a = $el.text().trim().split(" ");
a = a.map(function(_a) {
return "~" + _a;
});
return a;
}
return $el.text();
}
},
compareVal: function(l, n, key) {
if (key === 'tags' || key === 'audience') {
return _.xor(l, n).length > 0;
}
if (key === 'date-due') {
return l !== new Date(n);
}
return l !== n;
},
validateField: function($t, id, key, val) {
var i, valid;
valid = 1;
if (key === 'date-due') {
if (isNaN(val)) {
valid = 0;
}
}
if (key === 'audience') {
i = _.filter(val, function(a) {
if (a[0] !== "~") {
return 0;
}
if (a.split("/").length < 2) {
return 0;
}
if (a.split("/")[0].length < 3 || a.split("/")[1].length < 3) {
return 0;
}
return 1;
});
if (i.length !== val.length) {
valid = 0;
}
}
return valid;
},
_keyUp: function(e) {
var $t, id, key, val;
$t = $(e.target).closest('.field');
id = $t.closest('.item').attr('data-id');
key = $t.attr('data-key');
val = this.getVal($t.find('.input'), key);
if (this.compareVal(this.props.item[key], val, key)) {
if (!this.validateField($t, id, key, val)) {
$t.addClass('invalid');
return;
}
$t.removeClass('invalid');
if (this.to) {
clearTimeout(this.to);
}
return this.to = setTimeout(function() {
return WorkActions.changeItem(id, key, val);
}, 1000);
}
},
_focus: function(e) {
return this.props._focus(e, this);
},
_markDone: function(e) {
var id;
id = $(e.target).closest('.item').attr('data-id');
return WorkActions.changeItem(id, 'done', true);
},
formatDate: function(d) {
return (d.getDate()) + "-" + (d.getMonth() + 1) + "-" + (d.getFullYear());
if (d === null) {
return "";
}
return "~" + (d.getFullYear()) + "." + (d.getMonth() + 1) + "." + (d.getDate());
},
formatAudience: function(a) {
return a.join(" ").replace(/\~/g, "");
},
getInitialState: function() {
return {
@ -231,39 +330,53 @@ module.exports = recl({
return div({
className: itemClass,
draggable: true,
'data-id': this.props.item.id,
'data-index': this.props.index,
onDragStart: this._dragStart,
onDragEnd: this._dragEnd,
'data-index': this.props.index
onDragEnd: this._dragEnd
}, [
div({
className: 'audience'
}, this.props.item.audience.join(" ")), div({
className: 'audience field',
'data-key': 'audience'
}, [
div({
contentEditable: true,
className: 'input ib',
onKeyUp: this._keyUp
}, this.formatAudience(this.props.item.audience))
]), div({
className: 'sort ib top'
}, this.props.item.sort), div({
className: 'done ib'
className: 'done ib',
onClick: this._markDone
}, ''), div({
className: 'title ib top'
className: 'title ib top field',
'data-key': 'title'
}, [
div({
contentEditable: true,
onFocus: this._focus,
onKeyDown: this._keyDown,
onKeyUp: this._keyUp,
className: 'input ib'
}, this.props.item.title)
]), div({
className: 'date ib top'
className: 'date ib top field',
'data-key': 'date-due'
}, [
div({
contentEditable: true,
className: 'input ib'
}, this.formatDate(this.props.item['date-created']))
className: 'input ib',
onKeyUp: this._keyUp
}, this.formatDate(this.props.item['date-due']))
]), div({
className: 'tags ib top'
className: 'tags ib top field',
'data-key': 'tags'
}, [
div({
contentEditable: true,
className: 'input ib'
className: 'input ib',
onKeyUp: this._keyUp
}, this.props.item.tags.join(" "))
]), div({
className: 'expand ib',
@ -279,10 +392,12 @@ module.exports = recl({
className: 'caret left'
}, "")
]), div({
className: "description"
className: 'description field',
'data-key': 'description'
}, [
textarea({
className: 'input ib'
className: 'input ib',
onKeyUp: this._keyUp
}, this.props.item.description)
]), div({
className: "hr"
@ -329,7 +444,6 @@ module.exports = recl({
});
},{"../actions/WorkActions.coffee":1}],4:[function(require,module,exports){
var FilterComponent, ItemComponent, ListeningComponent, SortComponent, WorkActions, WorkStore, div, h1, input, rece, recl, ref, textarea;
@ -543,7 +657,6 @@ module.exports = recl({
});
},{"../actions/WorkActions.coffee":1,"../stores/WorkStore.coffee":15,"./FilterComponent.coffee":2,"./ItemComponent.coffee":3,"./ListeningComponent.coffee":5,"./SortComponent.coffee":6}],5:[function(require,module,exports){
var div, h1, input, rece, recl, ref, textarea;
@ -562,7 +675,6 @@ module.exports = recl({
});
},{}],6:[function(require,module,exports){
var button, div, h1, label, rece, recl, ref;
@ -608,7 +720,6 @@ module.exports = recl({
});
},{}],7:[function(require,module,exports){
var ListComponent, div, input, rece, recl, ref, textarea;
@ -631,7 +742,6 @@ module.exports = recl({
});
},{"./ListComponent.coffee":4}],8:[function(require,module,exports){
var Dispatcher;
@ -653,7 +763,6 @@ module.exports = _.merge(new Dispatcher(), {
});
},{"flux":10}],9:[function(require,module,exports){
var WorkComponent;
@ -666,7 +775,6 @@ $(function() {
});
},{"./components/WorkComponent.coffee":7,"./util.coffee":16}],10:[function(require,module,exports){
/**
* Copyright (c) 2014-2015, Facebook, Inc.
@ -1046,7 +1154,6 @@ module.exports = {
};
},{}],15:[function(require,module,exports){
var Dispatcher, EventEmitter, WorkStore, _filters, _list, _listening, _sorts, assign;
@ -1062,8 +1169,8 @@ _list = [
sort: 0,
"date-created": new Date('2015-8-18'),
"date-modified": new Date('2015-8-18'),
"date-due": null,
owner: "talsur-todres",
"date-due": new Date('2015-8-18'),
owner: "~talsur-todres",
audience: ["~doznec/urbit-meta", "~doznec/tlon"],
status: "working",
tags: ['food', 'office'],
@ -1082,7 +1189,7 @@ _list = [
"date-created": new Date('2015-8-18'),
"date-modified": new Date('2015-8-18'),
"date-due": null,
owner: "talsur-todres",
owner: "~talsur-todres",
audience: ["~doznec/tlon"],
status: "working",
tags: ['home', 'office'],
@ -1095,7 +1202,7 @@ _list = [
"date-created": new Date('2015-8-18'),
"date-modified": new Date('2015-8-18'),
"date-due": null,
owner: "talsur-todres",
owner: "~talsur-todres",
audience: ["~doznec/tlon"],
status: "working",
tags: ['home'],
@ -1117,7 +1224,7 @@ _filters = {
_sorts = {
title: 0,
owner: 0,
date: 0,
"date-due": 0,
sort: 0
};
@ -1230,7 +1337,6 @@ WorkStore.dispatchToken = Dispatcher.register(function(p) {
module.exports = WorkStore;
},{"../dispatcher/Dispatcher.coffee":8,"events":17,"object-assign":13}],16:[function(require,module,exports){
module.exports = {
uuid32: function() {
@ -1289,7 +1395,6 @@ module.exports = {
};
},{}],17:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//

View File

@ -7,8 +7,8 @@ _list = [
sort:0
"date-created":new Date('2015-8-18')
"date-modified":new Date('2015-8-18')
"date-due":null
owner:"talsur-todres"
"date-due":new Date('2015-8-18')
owner:"~talsur-todres"
audience:["~doznec/urbit-meta","~doznec/tlon"]
status:"working"
tags:['food','office']
@ -27,7 +27,7 @@ _list = [
"date-created":new Date('2015-8-18')
"date-modified":new Date('2015-8-18')
"date-due":null
owner:"talsur-todres"
owner:"~talsur-todres"
audience:["~doznec/tlon"]
status:"working"
tags:['home','office']
@ -40,7 +40,7 @@ _list = [
"date-created":new Date('2015-8-18')
"date-modified":new Date('2015-8-18')
"date-due":null
owner:"talsur-todres"
owner:"~talsur-todres"
audience:["~doznec/tlon"]
status:"working"
tags:['home']
@ -57,7 +57,7 @@ _filters =
_sorts =
title:0
owner:0
date:0
"date-due":0
sort:0
WorkStore = assign {},EventEmitter.prototype,{