mirror of
https://github.com/urbit/shrub.git
synced 2025-01-06 04:07:23 +03:00
no work
This commit is contained in:
parent
2e72056fb5
commit
0c9da59537
@ -1,34 +0,0 @@
|
||||
::
|
||||
::
|
||||
:::: /hook/hymn/fab/talk/pub/
|
||||
::
|
||||
|%
|
||||
++ cdnj
|
||||
|= a=wall ^- marl
|
||||
%+ turn a
|
||||
|= lib=tape
|
||||
;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/{lib}");
|
||||
--
|
||||
::
|
||||
::::
|
||||
::
|
||||
^- manx
|
||||
;html
|
||||
;head
|
||||
;meta(charset "utf-8");
|
||||
;* %- cdnj :~
|
||||
"jquery/2.1.1/jquery.js"
|
||||
"lodash.js/2.4.1/lodash.min.js"
|
||||
"react/0.13.1/react.js"
|
||||
==
|
||||
;script(type "text/javascript", src "/~/as/own/~/at/home/lib/urb.js");
|
||||
;meta(name "viewport", content "width=device-width, height=device-height, ".
|
||||
"initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0");
|
||||
;link(type "text/css", rel "stylesheet", href "/home/pub/work/src/css/main.css");
|
||||
;title: Work
|
||||
==
|
||||
;body
|
||||
;div#c;
|
||||
;script(type "text/javascript", src "/home/pub/work/src/js/main.js");
|
||||
==
|
||||
==
|
@ -1,84 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-italic.woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-mediumitalic.woff");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bolditalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-super.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-superitalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-extralight.woff");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-light.woff");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-regular.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-bold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-black.woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
@ -1,503 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-italic.woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-mediumitalic.woff");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bolditalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-super.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-superitalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-extralight.woff");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-light.woff");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-regular.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-bold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-black.woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
input,
|
||||
label,
|
||||
button {
|
||||
font-family: "bau";
|
||||
font-size: 18px;
|
||||
}
|
||||
#c {
|
||||
position: absolute;
|
||||
top: 0rem;
|
||||
left: 50%;
|
||||
width: 34rem;
|
||||
margin-left: -17rem;
|
||||
margin-bottom: 12rem;
|
||||
}
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
}
|
||||
input,
|
||||
button,
|
||||
.input {
|
||||
outline: none;
|
||||
border: 0;
|
||||
}
|
||||
h1.leader:after {
|
||||
content: "—";
|
||||
margin-left: 0.6rem;
|
||||
}
|
||||
.ctrl .sort label,
|
||||
.ctrl .filter label {
|
||||
text-transform: capitalize;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.07rem;
|
||||
}
|
||||
.item .status {
|
||||
text-transform: uppercase;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.07rem;
|
||||
}
|
||||
.ctrl {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.ctrl h1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.ctrl .sorts,
|
||||
.ctrl .filters {
|
||||
margin-left: -0.3rem;
|
||||
}
|
||||
.ctrl .sorts {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.ctrl .sort,
|
||||
.ctrl .filter label {
|
||||
font-weight: 500;
|
||||
}
|
||||
.ctrl .filter {
|
||||
line-height: 2rem;
|
||||
margin-right: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ctrl .sort {
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.ctrl .filter label {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
.ctrl .sort label,
|
||||
.ctrl .filter label {
|
||||
line-height: 2rem;
|
||||
}
|
||||
.ctrl .sort.s-1 label,
|
||||
.ctrl .sort.s--1 label {
|
||||
margin-right: 0.3rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.input-bool {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.input-bool.true,
|
||||
.input-bool.false {
|
||||
opacity: 1;
|
||||
}
|
||||
.input-bool,
|
||||
.item .done {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border: 0.2rem solid #ccc;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
.input-bool.true,
|
||||
.item .done-true {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.items {
|
||||
margin-top: 2rem;
|
||||
margin-left: -2rem;
|
||||
}
|
||||
.item {
|
||||
display: block;
|
||||
min-height: 3rem;
|
||||
max-height: 3rem;
|
||||
margin-bottom: 1.5rem;
|
||||
width: 36rem;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
transition: max-height 200ms linear;
|
||||
}
|
||||
.item .description,
|
||||
.item .discussion {
|
||||
transition: visibility 0s linear 200ms;
|
||||
visibility: hidden;
|
||||
}
|
||||
.item.expand {
|
||||
max-height: 24rem;
|
||||
transition: max-height 200ms linear;
|
||||
}
|
||||
.item.expand .description,
|
||||
.item.expand .discussion {
|
||||
transition: visibility 0s;
|
||||
visibility: visible;
|
||||
}
|
||||
.item .expand {
|
||||
margin-left: 2rem;
|
||||
cursor: pointer;
|
||||
transform-origin: 6px 12px;
|
||||
transition: transform 200ms linear;
|
||||
-webkit-transform-origin: 6px 12px;
|
||||
}
|
||||
.item.expand .expand {
|
||||
transform: rotate(90deg);
|
||||
-webkit-transform: rotate(90deg);
|
||||
transition: transform 200ms linear;
|
||||
}
|
||||
.item .header {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.item .sort,
|
||||
.item .title,
|
||||
.item .date,
|
||||
.item .tags,
|
||||
.item .comment {
|
||||
line-height: 2rem;
|
||||
}
|
||||
.item .sort,
|
||||
.item .date {
|
||||
font-family: 'scp';
|
||||
}
|
||||
.item .creator,
|
||||
.item .audience,
|
||||
.item .date,
|
||||
.item .ship {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.item .sort {
|
||||
opacity: 0;
|
||||
width: 1rem;
|
||||
height: 0.2rem;
|
||||
background-color: #e0e0e0;
|
||||
border-top: 0.2rem solid #ccc;
|
||||
border-bottom: 0.2rem solid #ccc;
|
||||
margin: 0 0.5rem;
|
||||
margin-top: -0.6rem;
|
||||
}
|
||||
.item:hover .sort {
|
||||
opacity: 1;
|
||||
}
|
||||
.item .done {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.item .creator,
|
||||
.item .audience,
|
||||
.item .date,
|
||||
.item .ship {
|
||||
text-transform: uppercase;
|
||||
height: 1rem;
|
||||
letter-spacing: 0.07rem;
|
||||
}
|
||||
.item .creator {
|
||||
max-width: 7rem;
|
||||
margin: 0 1rem 0 2rem;
|
||||
}
|
||||
.item .label {
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
}
|
||||
.item:hover .action-true .label,
|
||||
.item .action {
|
||||
display: none;
|
||||
}
|
||||
.item:hover .action-true .action {
|
||||
display: inline-block;
|
||||
}
|
||||
.item .audience {
|
||||
margin-left: 2.2rem;
|
||||
width: 24rem;
|
||||
}
|
||||
.item .audience .input {
|
||||
background-color: transparent;
|
||||
}
|
||||
.item .title,
|
||||
.item .date,
|
||||
.item .tags {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
height: 2rem;
|
||||
}
|
||||
.item .title {
|
||||
width: 16rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
.item .date,
|
||||
.item .tags {
|
||||
max-width: 6rem;
|
||||
min-width: 6rem;
|
||||
}
|
||||
.item .description,
|
||||
.item .discussion {
|
||||
line-height: 2rem;
|
||||
margin: 0.5rem 0 0.5rem 2rem;
|
||||
}
|
||||
.item .discussion {
|
||||
max-height: 9rem;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.item .description textarea {
|
||||
min-width: 32rem;
|
||||
min-height: 6rem;
|
||||
}
|
||||
.item .comp {
|
||||
width: 3rem;
|
||||
opacity: 0;
|
||||
}
|
||||
.item .comp .a {
|
||||
display: block;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
line-height: 0.9rem;
|
||||
}
|
||||
.item:hover .comp {
|
||||
opacity: 1;
|
||||
}
|
||||
.item .submit {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
background-color: #b9b9b9;
|
||||
letter-spacing: 0.07rem;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
padding: 0.6rem 0.3rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.comment {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.comment .date,
|
||||
.comment .ship {
|
||||
max-width: 12rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.comment .body {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.comment.new .input {
|
||||
max-width: 31rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
/* global */
|
||||
.top {
|
||||
vertical-align: top;
|
||||
}
|
||||
.ib {
|
||||
display: inline-block;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.a {
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.input {
|
||||
padding: 0 0.6rem 0 0.3rem;
|
||||
background-color: #ececec;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
resize: none;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
textarea:focus,
|
||||
.input:focus {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.invalid .input {
|
||||
color: #f00;
|
||||
}
|
||||
.caret.left {
|
||||
border-left: 12px solid #000;
|
||||
border-top: 9px solid transparent;
|
||||
border-right: 9px solid transparent;
|
||||
border-bottom: 9px solid transparent;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
.s-1 .caret,
|
||||
.caret.down {
|
||||
border-left: 6px solid transparent;
|
||||
border-top: 6px solid #fff;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
}
|
||||
.s--1 .caret,
|
||||
.caret.up {
|
||||
border-left: 6px solid transparent;
|
||||
border-top: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
}
|
||||
.hr1 {
|
||||
height: 0.2rem;
|
||||
width: 6rem;
|
||||
background-color: #ccc;
|
||||
margin-left: 2.2rem;
|
||||
}
|
||||
.hr2 {
|
||||
height: 0.1rem;
|
||||
width: 3rem;
|
||||
background-color: #ccc;
|
||||
}
|
||||
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
|
||||
#c {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
left: 0.6rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
input,
|
||||
textarea {
|
||||
-webkit-appearance: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
.input {
|
||||
padding: 0 0.3rem 0 0.1rem;
|
||||
}
|
||||
.ctrl {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.ctrl .sorts,
|
||||
.ctrl .filters {
|
||||
margin-left: 0;
|
||||
}
|
||||
.ctrl .filters .done label {
|
||||
display: none;
|
||||
}
|
||||
.ctrl .sort {
|
||||
padding: 0.2rem 0.3rem;
|
||||
}
|
||||
.ctrl .filter {
|
||||
line-height: 1.3rem;
|
||||
}
|
||||
.ctrl .sort label,
|
||||
.ctrl .filter label {
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.05rem;
|
||||
line-height: 1.3rem;
|
||||
}
|
||||
.items {
|
||||
margin-left: 0;
|
||||
}
|
||||
.item {
|
||||
width: auto;
|
||||
}
|
||||
.item .sort {
|
||||
display: none;
|
||||
}
|
||||
.item .title,
|
||||
.item .date,
|
||||
.item .tags {
|
||||
min-width: 4rem;
|
||||
max-width: 6rem;
|
||||
overflow: hidden;
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
.item .expand {
|
||||
float: right;
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
.item .description,
|
||||
.item .discussion {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.item .description textarea {
|
||||
min-width: 18rem;
|
||||
margin: 0;
|
||||
}
|
||||
.item .comment.new {
|
||||
max-width: 18rem;
|
||||
}
|
||||
.item .submit {
|
||||
line-height: 1.6rem;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
@ -1,352 +0,0 @@
|
||||
//
|
||||
// fonts first
|
||||
//
|
||||
|
||||
@import 'fonts'
|
||||
|
||||
html
|
||||
body
|
||||
input
|
||||
label
|
||||
button
|
||||
font-family "bau"
|
||||
font-size 18px
|
||||
|
||||
#c
|
||||
position absolute
|
||||
top 0rem
|
||||
left 50%
|
||||
width 34rem
|
||||
margin-left -17rem
|
||||
margin-bottom 12rem
|
||||
|
||||
h1
|
||||
font-weight 500
|
||||
|
||||
input
|
||||
button
|
||||
.input
|
||||
outline none
|
||||
border 0
|
||||
|
||||
h1.leader:after
|
||||
content "—"
|
||||
margin-left .6rem
|
||||
|
||||
.ctrl .sort label
|
||||
.ctrl .filter label
|
||||
text-transform capitalize
|
||||
font-size 1rem
|
||||
letter-spacing .07rem
|
||||
|
||||
.item .status
|
||||
text-transform uppercase
|
||||
font-size .7rem
|
||||
letter-spacing .07rem
|
||||
|
||||
.ctrl
|
||||
margin-top 2rem
|
||||
|
||||
h1
|
||||
font-size 1rem
|
||||
|
||||
.sorts
|
||||
.filters
|
||||
margin-left -.3rem
|
||||
|
||||
.sorts
|
||||
margin-top 1rem
|
||||
|
||||
.sort
|
||||
.filter label
|
||||
font-weight 500
|
||||
|
||||
.filter
|
||||
line-height 2rem
|
||||
margin-right 1rem
|
||||
vertical-align middle
|
||||
|
||||
.sort
|
||||
border 0
|
||||
cursor pointer
|
||||
background-color #000
|
||||
color #fff
|
||||
margin-right 1rem
|
||||
|
||||
.filter label
|
||||
margin-right .3rem
|
||||
|
||||
.sort label
|
||||
.filter label
|
||||
line-height 2rem
|
||||
|
||||
.sort.s-1
|
||||
.sort.s--1
|
||||
label
|
||||
margin-right .3rem
|
||||
cursor pointer
|
||||
|
||||
.input-bool
|
||||
opacity .3
|
||||
|
||||
.input-bool.true
|
||||
.input-bool.false
|
||||
opacity 1
|
||||
|
||||
|
||||
.input-bool
|
||||
.item .done
|
||||
width 1rem
|
||||
height 1rem
|
||||
border .2rem solid #ccc
|
||||
padding 0
|
||||
outline none
|
||||
background-color #fff
|
||||
|
||||
.input-bool.true
|
||||
.item .done-true
|
||||
background-color #ccc
|
||||
|
||||
.items
|
||||
margin-top 2rem
|
||||
margin-left -2rem
|
||||
|
||||
.item
|
||||
display block
|
||||
min-height 3rem
|
||||
max-height 3rem
|
||||
margin-bottom 1.5rem
|
||||
width 36rem
|
||||
overflow hidden
|
||||
background-color #fff
|
||||
transition max-height 200ms linear
|
||||
|
||||
.description
|
||||
.discussion
|
||||
transition visibility 0s linear 200ms
|
||||
visibility hidden
|
||||
|
||||
&.expand
|
||||
max-height 24rem
|
||||
transition max-height 200ms linear
|
||||
|
||||
.description
|
||||
.discussion
|
||||
transition visibility 0s
|
||||
visibility visible
|
||||
|
||||
.expand
|
||||
margin-left 2rem
|
||||
cursor pointer
|
||||
transform-origin 6px 12px
|
||||
transition transform 200ms linear
|
||||
-webkit-transform-origin 6px 12px
|
||||
|
||||
&.expand .expand
|
||||
transform rotate(90deg)
|
||||
-webkit-transform rotate(90deg)
|
||||
transition transform 200ms linear
|
||||
|
||||
.header
|
||||
display block
|
||||
width 100%
|
||||
height 1rem
|
||||
overflow hidden
|
||||
white-space nowrap
|
||||
|
||||
.sort
|
||||
.title
|
||||
.date
|
||||
.tags
|
||||
.comment
|
||||
line-height 2rem
|
||||
|
||||
.sort
|
||||
.date
|
||||
font-family 'scp'
|
||||
|
||||
.creator
|
||||
.audience
|
||||
.date
|
||||
.ship
|
||||
font-size .7rem
|
||||
|
||||
.sort
|
||||
opacity 0
|
||||
width 1rem
|
||||
height .2rem
|
||||
background-color #e0e0e0
|
||||
border-top .2rem solid #ccc
|
||||
border-bottom .2rem solid #ccc
|
||||
margin 0 .5rem
|
||||
margin-top -.6rem
|
||||
|
||||
&:hover .sort
|
||||
opacity 1
|
||||
|
||||
.done
|
||||
margin-top .5rem
|
||||
|
||||
.creator
|
||||
.audience
|
||||
.date
|
||||
.ship
|
||||
text-transform uppercase
|
||||
height 1rem
|
||||
letter-spacing .07rem
|
||||
|
||||
.creator
|
||||
max-width 7rem
|
||||
margin 0 1rem 0 2rem
|
||||
|
||||
.label
|
||||
font-style italic
|
||||
color #555
|
||||
|
||||
&:hover .action-true .label
|
||||
.action
|
||||
display none
|
||||
|
||||
&:hover .action-true .action
|
||||
display inline-block
|
||||
|
||||
.audience
|
||||
margin-left 2.2rem
|
||||
width 24rem
|
||||
|
||||
.audience .input
|
||||
background-color transparent
|
||||
|
||||
.title
|
||||
.date
|
||||
.tags
|
||||
overflow hidden
|
||||
white-space nowrap
|
||||
height 2rem
|
||||
|
||||
.title
|
||||
width 16rem
|
||||
margin-left .5rem
|
||||
|
||||
.date
|
||||
.tags
|
||||
max-width 6rem
|
||||
min-width 6rem
|
||||
|
||||
.description
|
||||
.discussion
|
||||
line-height 2rem
|
||||
margin .5rem 0 .5rem 2rem
|
||||
|
||||
.discussion
|
||||
max-height 9rem
|
||||
overflow-x hidden
|
||||
overflow-y scroll
|
||||
|
||||
.description textarea
|
||||
min-width 32rem
|
||||
min-height 6rem
|
||||
|
||||
.comp
|
||||
width 3rem
|
||||
opacity 0
|
||||
.a
|
||||
display block
|
||||
font-size .7rem
|
||||
font-weight 500
|
||||
line-height .9rem
|
||||
|
||||
&:hover .comp
|
||||
opacity 1
|
||||
|
||||
.submit
|
||||
font-size .7rem
|
||||
font-weight 500
|
||||
display inline-block
|
||||
background-color #b9b9b9
|
||||
letter-spacing .07rem
|
||||
color #fff
|
||||
border 0
|
||||
padding .6rem .3rem
|
||||
margin-top .5rem
|
||||
|
||||
.comment
|
||||
margin-bottom 1rem
|
||||
|
||||
.date
|
||||
.ship
|
||||
max-width 12rem
|
||||
margin-right 1rem
|
||||
|
||||
.body
|
||||
margin-top 1rem
|
||||
|
||||
&.new .input
|
||||
max-width 31rem
|
||||
margin-top 1rem
|
||||
|
||||
/* global */
|
||||
|
||||
.top
|
||||
vertical-align top
|
||||
|
||||
.ib
|
||||
display inline-block
|
||||
|
||||
.hidden
|
||||
display none
|
||||
|
||||
.a
|
||||
display inline
|
||||
cursor pointer
|
||||
text-decoration underline
|
||||
|
||||
.input
|
||||
padding 0 .6rem 0 .3rem
|
||||
background-color #ececec
|
||||
border 0
|
||||
font inherit
|
||||
resize none
|
||||
-webkit-user-select text
|
||||
user-select text
|
||||
|
||||
textarea:focus
|
||||
.input:focus
|
||||
background-color #e6e6e6
|
||||
|
||||
.invalid .input
|
||||
color red
|
||||
|
||||
.caret.left
|
||||
border-left 12px solid #000
|
||||
border-top 9px solid transparent
|
||||
border-right 9px solid transparent
|
||||
border-bottom 9px solid transparent
|
||||
margin-top .4rem
|
||||
|
||||
.s-1 .caret
|
||||
.caret.down
|
||||
border-left 6px solid transparent
|
||||
border-top 6px solid #fff
|
||||
border-right 6px solid transparent
|
||||
border-bottom 6px solid transparent
|
||||
|
||||
.s--1 .caret
|
||||
.caret.up
|
||||
border-left 6px solid transparent
|
||||
border-top 6px solid transparent
|
||||
border-right 6px solid transparent
|
||||
border-bottom 6px solid #fff
|
||||
|
||||
.hr1
|
||||
height .2rem
|
||||
width 6rem
|
||||
background-color #ccc
|
||||
margin-left 2.2rem
|
||||
|
||||
.hr2
|
||||
height .1rem
|
||||
width 3rem
|
||||
background-color #ccc
|
||||
|
||||
@import 'mobile'
|
@ -1,86 +0,0 @@
|
||||
/* computers --------*/
|
||||
// @media only screen and (min-width: 1024px)
|
||||
|
||||
|
||||
/* laptops / small screens ----------- */
|
||||
// @media only screen and (max-width: 1170px)
|
||||
|
||||
|
||||
/* tablets + phones ----------- */
|
||||
// @media only screen and (min-width: 320px) and (max-width: 1024px)
|
||||
|
||||
/* phones portrait and landscape ----------- */
|
||||
@media only screen and (min-device-width: 320px) and (max-device-width: 480px)
|
||||
#c
|
||||
width 100%
|
||||
margin 0
|
||||
left .6rem
|
||||
|
||||
h1
|
||||
font-size 1rem
|
||||
|
||||
input
|
||||
textarea
|
||||
-webkit-appearance none
|
||||
border-radius 0
|
||||
|
||||
.input
|
||||
padding 0 .3rem 0 .1rem
|
||||
|
||||
.ctrl
|
||||
margin-top 1rem
|
||||
|
||||
.ctrl .sorts
|
||||
.ctrl .filters
|
||||
margin-left 0
|
||||
|
||||
.ctrl .filters .done label
|
||||
display none
|
||||
|
||||
.ctrl .sort
|
||||
padding .2rem .3rem
|
||||
|
||||
.ctrl .filter
|
||||
line-height 1.3rem
|
||||
|
||||
.ctrl .sort label
|
||||
.ctrl .filter label
|
||||
font-size .7rem
|
||||
letter-spacing .05rem
|
||||
line-height 1.3rem
|
||||
|
||||
.items
|
||||
margin-left 0
|
||||
|
||||
.item
|
||||
width auto
|
||||
|
||||
.item .sort
|
||||
display none
|
||||
|
||||
.item .title
|
||||
.item .date
|
||||
.item .tags
|
||||
min-width 4rem
|
||||
max-width 6rem
|
||||
overflow hidden
|
||||
margin-left .3rem
|
||||
|
||||
.item .expand
|
||||
float right
|
||||
margin-right .6rem
|
||||
|
||||
.item .description
|
||||
.item .discussion
|
||||
margin .5rem 0
|
||||
|
||||
.item .description textarea
|
||||
min-width 18rem
|
||||
margin 0
|
||||
|
||||
.item .comment.new
|
||||
max-width 18rem
|
||||
|
||||
.item .submit
|
||||
line-height 1.6rem
|
||||
letter-spacing 0
|
@ -1,78 +0,0 @@
|
||||
Dispatcher = require '../dispatcher/Dispatcher.coffee'
|
||||
Persistence = require '../persistence/Persistence.coffee'
|
||||
{uuid32} = require '../util.coffee'
|
||||
|
||||
module.exports =
|
||||
newItem: ({before,after},_item={}) ->
|
||||
item =
|
||||
date_created: Date.now()
|
||||
date_modified: Date.now()
|
||||
creator: window.urb.ship
|
||||
version: -1
|
||||
id: _item.id ? uuid32()
|
||||
date_due: _item.date_due ? null
|
||||
done: _item.done ? null
|
||||
doer: _item.doer ? null
|
||||
tags: _item.tags ? []
|
||||
title: _item.title ? ''
|
||||
description: _item.description ? ''
|
||||
discussion: _item.discussion ? []
|
||||
audience: _item.audience ?
|
||||
[window.util.talk.mainStationPath window.urb.ship]
|
||||
if item.date_due or item.title or item.description
|
||||
item.version++
|
||||
Persistence.put new:item
|
||||
Dispatcher.handleViewAction {type:'newItem', before, after, item}
|
||||
|
||||
setItem: ({id,version},key,val) ->
|
||||
version += 1
|
||||
key = key.split('_').join '-'
|
||||
set = "#{key}": val
|
||||
Persistence.put old:{id,dif:{set}}
|
||||
Dispatcher.handleViewAction {type:'updateItem',id,version,key,val}
|
||||
|
||||
ownItem: ({id,version},act) ->
|
||||
version += 1
|
||||
Persistence.put old:{id,version,dif:doer:"#{act}":null}
|
||||
|
||||
removeItem: ({id,version}) ->
|
||||
if version >= 0
|
||||
Persistence.put old:{id,dif:set:audience:[]}
|
||||
Dispatcher.handleViewAction {type:'archiveItem',id}
|
||||
|
||||
setAudience: ({id},to) ->
|
||||
Persistence.put old:{id,dif:set:audience:to}
|
||||
Dispatcher.handleViewAction {type:'setAudienece',id,to}
|
||||
|
||||
addComment: ({id,version},val) ->
|
||||
version += 1
|
||||
Persistence.put old:{id,version,dif:add:comment:val}
|
||||
|
||||
setFilter: (key,val,filters) ->
|
||||
Dispatcher.handleViewAction {type:'setFilter', key,val}
|
||||
filters[key] = val
|
||||
Persistence.setLocal 'filters',filters
|
||||
|
||||
setSort: (key,val,sorts) ->
|
||||
Dispatcher.handleViewAction {type:'setSort',key,val}
|
||||
sorts[key] = val
|
||||
Persistence.setLocal 'sorts',sorts
|
||||
|
||||
moveItem: (list,to,from) ->
|
||||
Persistence.put {sort:list}
|
||||
Dispatcher.handleViewAction {type:'moveItems',list,to,from}
|
||||
|
||||
getLocal: (key)->
|
||||
Persistence.getLocal key,(e,r) ->
|
||||
new Error(e) if e
|
||||
return if r is null
|
||||
obj =
|
||||
type:"load#{key[0].toUpperCase()+key.slice(1)}"
|
||||
obj[key] = r
|
||||
Dispatcher.handleServerAction obj
|
||||
|
||||
listenList: (type)->
|
||||
Persistence.subscribe type, (err,d)->
|
||||
if d?
|
||||
{sort,tasks} = d.data
|
||||
Dispatcher.handleServerAction {type:"getData",sort,tasks}
|
@ -1,95 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,textarea} = React.DOM
|
||||
|
||||
WorkActions = require '../actions/WorkActions.coffee'
|
||||
|
||||
module.exports = recl
|
||||
displayName: 'Field'
|
||||
getInitialState: -> invalid:no
|
||||
shouldComponentUpdate: (props)->
|
||||
while @oldValue?.length
|
||||
if @oldValue[0] is props.defaultValue
|
||||
return false
|
||||
else @oldValue.shift()
|
||||
true
|
||||
|
||||
render: ->
|
||||
className = "#{@props.className ? @props._key} field ib"
|
||||
if @state.invalid then className += " invalid"
|
||||
|
||||
props = _.extend {}, @props, {
|
||||
@onKeyUp
|
||||
ref:'input'
|
||||
defaultValue: @props.render @props.defaultValue
|
||||
className: 'input ib'
|
||||
}
|
||||
|
||||
div {className},
|
||||
if @props.textarea then (textarea props)
|
||||
else
|
||||
props.contentEditable = true
|
||||
props.dangerouslySetInnerHTML =
|
||||
__html: $('<div>').text(props.defaultValue).html()
|
||||
(div props)
|
||||
|
||||
onKeyUp: (e) ->
|
||||
$t = $(e.target).closest '.field'
|
||||
_val = @getVal()
|
||||
if @props.item.ghost and _val is ""
|
||||
return
|
||||
val = @parse _val
|
||||
unless @validate val
|
||||
@setState invalid:yes
|
||||
return
|
||||
@setState invalid:no
|
||||
|
||||
unless @equal @props.defaultValue, val
|
||||
@oldValue ?= []
|
||||
@oldValue.push val
|
||||
if @to then clearTimeout @to
|
||||
@to = setTimeout =>
|
||||
{item,_key,index} = @props
|
||||
if item.version >= 0
|
||||
WorkActions.setItem item, _key, val
|
||||
else WorkActions.newItem {},
|
||||
id: item.id
|
||||
tags: item.tags
|
||||
audience: item.audience
|
||||
"#{_key}": val
|
||||
,1000
|
||||
|
||||
getVal: ->
|
||||
if @props.textarea
|
||||
$(@refs.input.getDOMNode()).val()
|
||||
else $(@refs.input.getDOMNode()).text()
|
||||
|
||||
parse: (text)-> switch @props._key
|
||||
when 'tags' then text.trim().split(" ")
|
||||
when 'audience' then text.trim().split(" ").map (a) -> "~#{a}".toLowerCase()
|
||||
when 'date_due'
|
||||
d = text.slice(1).replace(/\./g, "-")
|
||||
return NaN if d.length < 8
|
||||
new Date(d).valueOf()
|
||||
else text
|
||||
|
||||
equal: (vol=(@parse ""),val) -> switch @props._key
|
||||
when 'tags', 'audience'
|
||||
(_.xor(vol,val).length is 0)
|
||||
when 'date_due'
|
||||
vol.valueOf() is val
|
||||
else vol is val
|
||||
|
||||
validate: (val) -> switch @props._key
|
||||
when 'date_due'
|
||||
!isNaN(val)
|
||||
when 'audience'
|
||||
for a in val
|
||||
[ship,station,rest...] = a.split("/")
|
||||
return no unless (rest.length is 0) and ship and station
|
||||
return no if ship[0] isnt "~"
|
||||
return no if ship < 3
|
||||
return no if station < 3
|
||||
yes
|
||||
else yes
|
||||
|
@ -1,60 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,h1,label,button} = React.DOM
|
||||
|
||||
module.exports = recl
|
||||
onClick: (e) ->
|
||||
switch @props.filters['done']
|
||||
when null
|
||||
b = true
|
||||
when true
|
||||
b = false
|
||||
when false
|
||||
b = null
|
||||
@props.onChange 'done',b
|
||||
onKeyDown: (e) ->
|
||||
if e.keyCode is 13
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
onKeyUp: (e) -> @change e
|
||||
onBlur: (e) -> @change e
|
||||
change: (e) ->
|
||||
$t = $(e.target).closest('.filter')
|
||||
txt = $t.find('.input').text().trim()
|
||||
key = $t.attr('data-key')
|
||||
if txt.length is 0 then txt = null
|
||||
else switch key
|
||||
when 'creator' then txt = "~#{txt}"
|
||||
when 'audience' then txt = txt.split " "
|
||||
when 'tags' then txt = [txt]
|
||||
@props.onChange key,txt
|
||||
|
||||
fields: [ {filter:'done', key:'done', title: ''},
|
||||
{filter:'owned', key:'creator', title: 'Owner:'},
|
||||
{filter:'doer', key:'doer', title: 'Doer:'}
|
||||
{filter:'tag', key:'tags', title: 'Tag:'},
|
||||
{filter:'channel', key:'audience', title: 'Audience:'},
|
||||
]
|
||||
|
||||
render: ->
|
||||
(div {className:'filters'}, @fields.map ({filter,key,title})=>
|
||||
txt = @props.filters[key]
|
||||
txt = txt?.replace(/\~/g,"") if key is 'creator'
|
||||
|
||||
(div {key, 'data-key':key, className:"#{filter} filter ib"},
|
||||
(label {}, title)
|
||||
switch filter
|
||||
when 'done'
|
||||
(button {
|
||||
className:'input-bool ib '+@props.filters[key],
|
||||
@onClick
|
||||
},"")
|
||||
else
|
||||
(div {
|
||||
contentEditable:true
|
||||
className:'input ib'
|
||||
@onKeyDown
|
||||
@onKeyUp
|
||||
@onBlur
|
||||
},txt)
|
||||
))
|
@ -1,162 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,textarea,button} = React.DOM
|
||||
|
||||
WorkActions = require '../actions/WorkActions.coffee'
|
||||
Field = require './FieldComponent.coffee'
|
||||
|
||||
module.exports = recl
|
||||
displayName: 'Item'
|
||||
onDragStart: (e) ->
|
||||
unless @props.draggable
|
||||
e.preventDefault()
|
||||
return
|
||||
$t = $(e.target)
|
||||
@dragged = $t.closest('.item')
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
e.dataTransfer.setData 'text/html',e.currentTarget
|
||||
@props._dragStart e,@
|
||||
|
||||
onDragEnd: (e) -> @props._dragEnd e,@
|
||||
|
||||
onKeyDown: (e) ->
|
||||
@props.title_keyDown e,@
|
||||
|
||||
kc = e.keyCode
|
||||
|
||||
switch kc
|
||||
# tab - expand
|
||||
when 9
|
||||
if @state.expand is false
|
||||
@setState {expand:true}
|
||||
# esc - collapse
|
||||
when 27
|
||||
@setState {expand:false}
|
||||
|
||||
if (kc is 9 and @state.expand is false) or (kc is 27)
|
||||
e.preventDefault()
|
||||
return
|
||||
|
||||
onFocus: (e) -> @props._focus e,@
|
||||
|
||||
_markDone: (e) ->
|
||||
WorkActions.setItem @props.item,'done',(not (@props.item.done is true))
|
||||
|
||||
getStatus: -> switch @props.item.doer
|
||||
when window.urb.ship then "owned"
|
||||
when undefined then ""
|
||||
when null then "available"
|
||||
else "taken: ~"+@props.item.doer
|
||||
|
||||
getAction: -> switch @props.item.doer
|
||||
when null
|
||||
action = "claim"
|
||||
when window.urb.ship
|
||||
action = "release"
|
||||
else ""
|
||||
|
||||
_changeStatus: (e) ->
|
||||
return if @props.item.status is 'released'
|
||||
if @props.item.status is 'accepted' and
|
||||
@formatCreator(@props.item.creator) isnt window.urb.ship
|
||||
return
|
||||
WorkActions.ownItem @props.item,@getAction()
|
||||
|
||||
_submitComment: (e) ->
|
||||
$input = $(e.target).closest('.item').find('.comment .input')
|
||||
val = $input.text()
|
||||
return if val.length is 0
|
||||
WorkActions.addComment @props.item,val
|
||||
$input.text('')
|
||||
|
||||
formatDate: (d,l) ->
|
||||
unless d
|
||||
return ""
|
||||
_d = "~#{d.getFullYear()}.#{(d.getMonth()+1)}.#{d.getDate()}"
|
||||
if l
|
||||
_d += "..#{d.getHours()}.#{d.getMinutes()}.#{d.getSeconds()}"
|
||||
_d
|
||||
|
||||
formatCreator: (o="") -> o.replace /\~/g,""
|
||||
|
||||
formatAudience: (a=[]) -> @formatCreator a.join(" ")
|
||||
|
||||
getInitialState: -> {expand:false}
|
||||
|
||||
renderField: (_key,props,render=_.identity)->
|
||||
{item,index} = @props
|
||||
defaultValue = item[_key]
|
||||
rece Field, $.extend props, {render,_key,defaultValue,item,index}
|
||||
|
||||
renderTopField: (key,props,format)->
|
||||
_props = _.extend props,{className:"#{props.className ? key} top"}
|
||||
@renderField key,_props,format
|
||||
|
||||
componentDidMount: ->
|
||||
setInterval =>
|
||||
$('.new.comment .date').text @formatDate new Date
|
||||
, 1000
|
||||
|
||||
render: ->
|
||||
itemClass = 'item'
|
||||
if @state.expand then itemClass += ' expand'
|
||||
|
||||
discussion = _.clone @props.item.discussion ? []
|
||||
discussion.reverse()
|
||||
|
||||
status = @getStatus()
|
||||
action = @getAction()
|
||||
|
||||
(div {
|
||||
className:itemClass
|
||||
draggable:true
|
||||
@onDragStart,@onDragEnd
|
||||
},
|
||||
(div {
|
||||
className:'header'
|
||||
},
|
||||
(div {className:'creator ib'}, @formatCreator(@props.item.creator))
|
||||
(div {
|
||||
className:'status ib action-'+(action.length > 0)
|
||||
'data-key':'status'
|
||||
onClick:@_changeStatus
|
||||
},
|
||||
(div {className:'label'}, status)
|
||||
(div {className:'action a'}, action)
|
||||
)
|
||||
(@renderField 'audience', {}, @formatAudience)
|
||||
)
|
||||
(div {className:'sort ib'}, '')
|
||||
(button {className:'done ib done-'+(@props.item.done is true), onClick:@_markDone}, '')
|
||||
(@renderTopField 'title', {@onFocus,@onKeyDown})
|
||||
(@renderTopField 'date_due', {className:'date'}, @formatDate)
|
||||
(@renderTopField 'tags', {}, (tags=[])-> tags.join(" "))
|
||||
(div {
|
||||
className:'expand ib',
|
||||
onClick: (e) => @setState {expand:!@state.expand}
|
||||
}, (div {className:'caret left'},"")
|
||||
)
|
||||
(@renderField 'description',textarea: yes)
|
||||
|
||||
(div {className:"hr"},"")
|
||||
if discussion?
|
||||
(div {className:"discussion"},
|
||||
(div {className:"comments"}, discussion.map (slug) =>
|
||||
(div {className:'comment',key:slug.date},
|
||||
(div {className:'hr2'},"")
|
||||
(div {className:'ship ib'}, slug.ship)
|
||||
(div {className:'date ib'}, @formatDate slug.date,true)
|
||||
(div {className:'body'}, slug.body)
|
||||
)
|
||||
),
|
||||
(div {className:'new comment'},
|
||||
(div {className:'hr2'},"")
|
||||
(div {className:'ship ib'}, window.urb.ship)
|
||||
(div {className:'date ib'}, @formatDate new Date)
|
||||
(div {
|
||||
contentEditable:true,
|
||||
className:'input'})
|
||||
(button {className:'submit',onClick:@_submitComment},'Post')
|
||||
)
|
||||
)
|
||||
)
|
@ -1,175 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,h1,input,textarea} = React.DOM
|
||||
|
||||
WorkStore = require '../stores/WorkStore.coffee'
|
||||
WorkActions = require '../actions/WorkActions.coffee'
|
||||
ItemComponent = require './ItemComponent.coffee'
|
||||
|
||||
ListeningComponent = require './ListeningComponent.coffee'
|
||||
FilterComponent = require './FilterComponent.coffee'
|
||||
SortComponent = require './SortComponent.coffee'
|
||||
|
||||
module.exports = recl
|
||||
displayName: 'List'
|
||||
stateFromStore: -> window.canSort = WorkStore.canSort(); {
|
||||
list:WorkStore.getList()
|
||||
noNew:WorkStore.noNew()
|
||||
canSort:WorkStore.canSort()
|
||||
fulllist:WorkStore.getFullList()
|
||||
sorts:WorkStore.getSorts()
|
||||
filters:WorkStore.getFilters()
|
||||
expand:false
|
||||
updated:WorkStore.getUpdated()
|
||||
}
|
||||
|
||||
getInitialState: -> @stateFromStore()
|
||||
_onChangeStore: -> @setState @stateFromStore()
|
||||
|
||||
alias: ->
|
||||
@$el = $ @getDOMNode()
|
||||
@$items = @$el.find('.items').children()
|
||||
|
||||
_focus: (e,i) -> @setState {selected:i.props.index}
|
||||
|
||||
_dragStart: (e,i) -> @dragged = i.dragged
|
||||
|
||||
_dragEnd: (e,i) ->
|
||||
from = Number @dragged.closest('.item-wrap').attr('data-index')
|
||||
to = Number @over.closest('.item-wrap').attr('data-index')
|
||||
if from<to then to--
|
||||
if @drop is 'after' then to++
|
||||
sort = _.clone @state.list
|
||||
sort.splice to, 0, sort.splice(from,1)[0]
|
||||
list = (id for {id,version} in sort when (version ? -1) >= 0)
|
||||
WorkActions.moveItem list, to, from
|
||||
@dragged.removeClass 'hidden'
|
||||
@placeholder.remove()
|
||||
|
||||
_dragOver: (e,i) ->
|
||||
e.preventDefault()
|
||||
$t = $(e.target).closest('.item')
|
||||
if $t.hasClass 'placeholder' then return
|
||||
if $t.length is 0 then return
|
||||
@over = $t
|
||||
if not @dragged.hasClass('hidden') then @dragged.addClass 'hidden'
|
||||
if (e.clientY - $t[0].offsetTop) < ($t[0].offsetHeight / 2)
|
||||
@drop = 'before'
|
||||
@placeholder.insertBefore $t
|
||||
else
|
||||
@drop = 'after'
|
||||
@placeholder.insertAfter $t
|
||||
|
||||
title_keyDown: (e,i) -> switch e.keyCode
|
||||
# enter - add new
|
||||
when 13
|
||||
e.preventDefault()
|
||||
return if @state.noNew
|
||||
{item} = i.props
|
||||
after = null; before = null
|
||||
if window.getSelection().getRangeAt(0).endOffset is 0
|
||||
ins = @state.selected
|
||||
before = item.id
|
||||
else
|
||||
after = item.id
|
||||
ins = @state.selected+1 # XX consolidate
|
||||
@setState {selected:ins,select:true}
|
||||
unless item.ghost
|
||||
{tags,audience} = item
|
||||
item = {tags,audience}
|
||||
WorkActions.newItem {before,after}, item
|
||||
# backspace - remove if at 0
|
||||
when 8
|
||||
if (window.getSelection().getRangeAt(0).endOffset is 0)
|
||||
e.preventDefault()
|
||||
if (e.target.innerText.length is 0)
|
||||
if @state.selected isnt 0
|
||||
@setState {selected:@state.selected-1,select:"end"}
|
||||
WorkActions.removeItem i.props.item
|
||||
else if ({index} = i.props; index > 0) and
|
||||
(prev = @state.list[i.props.index - 1]; prev.version < 0)
|
||||
WorkActions.removeItem prev
|
||||
# up
|
||||
when 38
|
||||
e.preventDefault()
|
||||
last = @state.selected-1
|
||||
if last<0 then last = @state.list.length-1
|
||||
@$items.eq(last).find('.title .input').focus()
|
||||
@setState {select:"end"}
|
||||
# down
|
||||
when 40
|
||||
e.preventDefault()
|
||||
next = @state.selected+1
|
||||
if next is @state.list.length then next = 0
|
||||
@$items.eq(next).find('.title .input').focus()
|
||||
@setState {select:"end"}
|
||||
|
||||
_changeListening: ->
|
||||
|
||||
_changeFilter: (key,val) -> WorkActions.setFilter key,val,@state.filters
|
||||
|
||||
_changeSort: (key,val) -> WorkActions.setSort key,val,@state.sorts
|
||||
|
||||
componentDidMount: ->
|
||||
@placeholder = $ "<div class='item placeholder'><div class='sort'>x</div></div>"
|
||||
WorkStore.addChangeListener @_onChangeStore
|
||||
WorkActions.listenList @props.list
|
||||
WorkActions.getLocal 'filters'
|
||||
WorkActions.getLocal 'sorts'
|
||||
@alias()
|
||||
|
||||
componentDidUpdate: (_props,_state)->
|
||||
@alias()
|
||||
if @state.select
|
||||
$title = @$items.eq(@state.selected).find('.title .input')
|
||||
if @state.selected?
|
||||
$title.focus()
|
||||
if @state.select is "end"
|
||||
r = window.getSelection().getRangeAt(0)
|
||||
r.setStart $title[0],0
|
||||
r.setEnd $title[0],0
|
||||
s = window.getSelection()
|
||||
s.removeAllRanges()
|
||||
s.addRange r
|
||||
@setState {select:false}
|
||||
|
||||
render: ->
|
||||
(div {},
|
||||
(div {className:'ctrl'},
|
||||
rece(ListeningComponent, {
|
||||
listening:@state.listening
|
||||
onChange:@_changeListening
|
||||
})
|
||||
(div {className:'transforms'},
|
||||
rece(FilterComponent, {
|
||||
filters:@state.filters
|
||||
onChange:@_changeFilter
|
||||
})
|
||||
rece(SortComponent, {
|
||||
sorts:@state.sorts
|
||||
onChange:@_changeSort
|
||||
})
|
||||
)
|
||||
)
|
||||
(div {
|
||||
className:'items'
|
||||
onDragOver:@_dragOver
|
||||
}, _.map @state.list,(item,index) =>
|
||||
className = "item-wrap"
|
||||
key = item.id
|
||||
draggable = @state.canSort
|
||||
if item.ghost
|
||||
className += " ghost"
|
||||
draggable = false
|
||||
(div {className,key,'data-index':index},
|
||||
rece(ItemComponent,{
|
||||
item
|
||||
index
|
||||
draggable
|
||||
@_focus
|
||||
@title_keyDown
|
||||
@_dragStart
|
||||
@_dragEnd})
|
||||
)
|
||||
)
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,h1,input,textarea} = React.DOM
|
||||
|
||||
module.exports = recl
|
||||
render: ->
|
||||
(div {className:'listening'}, "")
|
@ -1,27 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,h1,button,label} = React.DOM
|
||||
|
||||
module.exports = recl
|
||||
onClick: (e) ->
|
||||
$t = $(e.target).closest '.sort'
|
||||
key = $t.attr('data-key')
|
||||
sor = Number $t.attr 'data-state'
|
||||
if sor is 0 then sor = 1
|
||||
else if sor is 1 then sor = -1
|
||||
else if sor is -1 then sor = 0
|
||||
@props.onChange key,sor
|
||||
|
||||
render: ->
|
||||
(div {className:'sorts'}, _.map @props.sorts,(state,key) =>
|
||||
(button {
|
||||
key
|
||||
@onClick
|
||||
'data-key':key
|
||||
'data-state':state
|
||||
className:"sort s-#{state}"
|
||||
},
|
||||
(label {}, key)
|
||||
(div {className:'caret ib'}, '')
|
||||
)
|
||||
)
|
@ -1,12 +0,0 @@
|
||||
recl = React.createClass
|
||||
rece = React.createElement
|
||||
{div,h1} = React.DOM
|
||||
|
||||
ListComponent = require './ListComponent.coffee'
|
||||
|
||||
module.exports = recl
|
||||
render: ->
|
||||
(div {},
|
||||
(h1 {className:'leader'}, "Work")
|
||||
(rece(ListComponent,{list:'upcoming'}))
|
||||
)
|
@ -1,13 +0,0 @@
|
||||
Dispatcher = require('flux').Dispatcher
|
||||
|
||||
module.exports = _.merge new Dispatcher(), {
|
||||
handleServerAction: (action) ->
|
||||
@dispatch
|
||||
source: 'server'
|
||||
action: action
|
||||
|
||||
handleViewAction: (action) ->
|
||||
@dispatch
|
||||
source: 'view'
|
||||
action: action
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
WorkComponent = require './components/WorkComponent.coffee'
|
||||
window.util = _.extend window.util || {}, require './util.coffee'
|
||||
|
||||
$ ->
|
||||
React.render React.createElement(WorkComponent),$('#c')[0]
|
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "urbit-work",
|
||||
"version": "0.0.0",
|
||||
"repository": {
|
||||
"type":"git",
|
||||
"url":"https://github.com/urbit/urbit"
|
||||
},
|
||||
"description": "urbit work frontend",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"coffeeify": "~0.7.0",
|
||||
"flux": "~2.0.1",
|
||||
"lodash": "~2.4.1",
|
||||
"moment-timezone": "~0.2.4",
|
||||
"object-assign": "^1.0.0"
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
urb.appl = 'work'
|
||||
listeners = {}
|
||||
cache = null
|
||||
urb.bind "/repo", (err,dat)->
|
||||
if err
|
||||
document.write err
|
||||
else
|
||||
cache = dat
|
||||
(cb null,dat) for k,cb of listeners
|
||||
module.exports =
|
||||
put: (update,cb) -> urb.send(update,{mark:'work-command'},cb)
|
||||
subscribe: (key,cb) ->
|
||||
listeners[key] = cb
|
||||
(cb null,cache) if cache?
|
||||
setLocal: (key,val) -> window.localStorage.setItem key,JSON.stringify val
|
||||
getLocal: (key,cb) ->
|
||||
try
|
||||
out = JSON.parse window.localStorage.getItem key
|
||||
cb null,out
|
||||
catch e
|
||||
cb e
|
@ -1,153 +0,0 @@
|
||||
EventEmitter = require('events').EventEmitter
|
||||
assign = require 'object-assign'
|
||||
Dispatcher = require '../dispatcher/Dispatcher.coffee'
|
||||
{uuid32} = require '../util.coffee'
|
||||
|
||||
_tasks = {}
|
||||
_list = []
|
||||
_listening = []
|
||||
_updated = Date.now()
|
||||
_filters =
|
||||
done:null
|
||||
creator:null
|
||||
doer:null
|
||||
audience:null
|
||||
tags:null
|
||||
_sorts =
|
||||
sort:0
|
||||
title:0
|
||||
creator:0
|
||||
date_due:0
|
||||
_ghost = id:uuid32()
|
||||
|
||||
WorkStore = assign {},EventEmitter.prototype,{
|
||||
emitChange: -> @emit 'change'
|
||||
addChangeListener: (cb) -> @on 'change', cb
|
||||
removeChangeListener: (cb) -> @removeListener "change", cb
|
||||
|
||||
getData: ({sort,tasks})->
|
||||
sort.map (id,index)=>
|
||||
unless _tasks[id]
|
||||
_list.splice index, 0, id
|
||||
if !tasks[id]
|
||||
console.log "lost", id
|
||||
else if !_tasks[id] or tasks[id].version > _tasks[id].version
|
||||
_tasks[id] = @itemFromData tasks[id], index
|
||||
_updated = Date.now()
|
||||
|
||||
getUpdated: -> _updated
|
||||
|
||||
getFullList: -> _list
|
||||
getList: (key) ->
|
||||
list = []
|
||||
for id in _list
|
||||
task = _tasks[id]
|
||||
if !task? or task.archived
|
||||
continue
|
||||
add = true
|
||||
for _k,_v of _filters
|
||||
if _v is null then continue
|
||||
c = task[_k]
|
||||
add = switch _k
|
||||
when 'tags', 'audience'
|
||||
_.intersection(c,_v).length isnt 0
|
||||
when 'creator'
|
||||
c is _v.replace(/\~/g, "")
|
||||
when 'done'
|
||||
!!c is _v
|
||||
else c is _v
|
||||
break unless add
|
||||
if add
|
||||
list.push task
|
||||
if _.uniq(_.values(_sorts)).length > 1
|
||||
for k,v of _sorts
|
||||
if v isnt 0
|
||||
break
|
||||
list = _.sortBy list,k,k
|
||||
if v is -1 then list.reverse()
|
||||
unless @noNew()
|
||||
ghost = $.extend {ghost:true,version:-1}, _ghost
|
||||
if _filters.tags then ghost.tags = _filters.tags
|
||||
if _filters.audience then ghost.audience = _filters.audience
|
||||
list.push ghost
|
||||
list
|
||||
|
||||
newItem: ({before,after,item}) ->
|
||||
if before
|
||||
index = _list.indexOf before
|
||||
if index is -1 then index = null
|
||||
if after
|
||||
index = 1 + _list.indexOf after
|
||||
if index is 0 then index = null
|
||||
|
||||
index ?= _list.length
|
||||
|
||||
if item.id is _ghost.id
|
||||
_ghost.id = uuid32()
|
||||
unless _tasks[item.id]?
|
||||
_list.splice index,0,item.id
|
||||
else if _tasks[item.id].version >= 0
|
||||
throw new Error "Collision: already have #{item.id}"
|
||||
_tasks[item.id] = @itemFromData item, index
|
||||
|
||||
loadFilters: ({filters}) ->
|
||||
console.log 'filters'
|
||||
console.log filters
|
||||
_filters = filters
|
||||
getFilters: -> _filters
|
||||
setFilter: ({key,val}) ->
|
||||
_filters[key] = val
|
||||
|
||||
loadSorts: ({sorts}) ->
|
||||
console.log 'load sorts'
|
||||
console.log sorts
|
||||
_sorts = sorts
|
||||
getSorts: -> _sorts
|
||||
setSort: ({key,val}) ->
|
||||
for k,v of _sorts
|
||||
_sorts[k] = 0
|
||||
_sorts[key] = val
|
||||
|
||||
canSort: ->
|
||||
for k,v of _sorts
|
||||
if k is "sort" and v is 1
|
||||
return true
|
||||
else if v isnt 0
|
||||
return false
|
||||
true
|
||||
|
||||
noNew: ->
|
||||
(_filters.done is true) or
|
||||
_filters.creator? and _filters.owner isnt urb.ship
|
||||
|
||||
itemFromData: (item,index=0)->
|
||||
_item = _.extend {sort:index}, item
|
||||
_item.date_modified = new Date item.date_modified
|
||||
_item.date_created = new Date item.date_created
|
||||
_item.date_due = new Date item.date_due if item.date_due?
|
||||
_item.done = new Date item.done if item.done?
|
||||
_item.discussion = item.discussion.map ({ship,body,date}) ->
|
||||
{ship,body,date: new Date date}
|
||||
_item
|
||||
|
||||
moveItems: ({list,to,from}) ->
|
||||
_tasks[_list[from]].sort = _tasks[_list[to]].sort
|
||||
_list = list
|
||||
setAudience: ({id,to})-> _tasks[id].audience = to
|
||||
archiveItem: ({id})-> _tasks[id].archived = true
|
||||
updateItem: ({id,version,key,val})->
|
||||
_tasks[id].version = version
|
||||
_tasks[id].done = val if key is 'done'
|
||||
|
||||
}
|
||||
|
||||
WorkStore.setMaxListeners 100
|
||||
|
||||
WorkStore.dispatchToken = Dispatcher.register (p) ->
|
||||
a = p.action
|
||||
|
||||
if WorkStore[a.type]
|
||||
WorkStore[a.type] a
|
||||
WorkStore.emitChange()
|
||||
|
||||
module.exports = WorkStore
|
@ -1,38 +0,0 @@
|
||||
module.exports =
|
||||
uuid32: ->
|
||||
vals = for i in [0..5]
|
||||
str = Math.ceil(Math.random()*10000000).toString(32)
|
||||
("00000"+str).substr(-5,5)
|
||||
vals.unshift Math.ceil(Math.random()*8)
|
||||
"0v" + vals.join '.'
|
||||
|
||||
getScroll: ->
|
||||
@writingPosition = $('#c').outerHeight(true)+$('#c').offset().top-$(window).height()
|
||||
|
||||
setScroll: ->
|
||||
window.util.getScroll()
|
||||
$(window).scrollTop($("#c").height())
|
||||
|
||||
isScrolling: ->
|
||||
if not window.util.writingPosition
|
||||
window.util.getScroll()
|
||||
return ($(window).scrollTop()+$('#writing').outerHeight() < window.util.writingPosition)
|
||||
|
||||
checkScroll: ->
|
||||
if window.util.isScrolling()
|
||||
$('body').addClass 'scrolling'
|
||||
else
|
||||
$('body').removeClass 'scrolling'
|
||||
talk:
|
||||
mainStations: ["court","floor","porch"]
|
||||
mainStationPath: (user) -> "~#{user}/#{window.util.talk.mainStation(user)}"
|
||||
mainStation: (user) ->
|
||||
if not user then user = window.urb.user
|
||||
switch user.length
|
||||
when 3
|
||||
return "court"
|
||||
when 6
|
||||
return "floor"
|
||||
when 13
|
||||
return "porch"
|
||||
|
@ -1,8 +0,0 @@
|
||||
0v0_~1999.1.1 2~1999.1.2 ~1999.5.20
|
||||
Tagged!
|
||||
Yoooo
|
||||
~fyr>~zod
|
||||
Testin
|
||||
~doznec ~2015.1.3
|
||||
how long has
|
||||
this been around?
|
Loading…
Reference in New Issue
Block a user