Commit a9d6471cd7bbf46847dddc28bef3d820a7a66620
(re)factor out time-picker component
mixmix committed on 8/27/2018, 5:21:48 AMParent: f59aead1575acfc06a8dea2a412a0469297656b1
Files changed
views/new.js | changed |
views/new.mcss | changed |
views/component/time-picker.js | added |
views/component/time-picker.mcss | added |
views/new.js | ||
---|---|---|
@@ -1,6 +1,7 @@ | ||
1 | 1 … | const { h, Value, Struct, Array: MutantArray, when, computed } = require('mutant') |
2 | 2 … | const Marama = require('marama') |
3 … | +const TimePicker = require('./component/time-picker.js') | |
3 | 4 … | |
4 | 5 … | module.exports = function ScryNew (opts) { |
5 | 6 … | const { |
6 | 7 … | // i18n |
@@ -40,17 +41,9 @@ | ||
40 | 41 … | h('div.instruction', 'Select one or multiple dates') |
41 | 42 … | ]), |
42 | 43 … | h('div.time-picker', [ |
43 | 44 … | h('label', computed(state.days, days => `Same times for all dates (${days.length})`)), |
44 | - h('div.picker', [ | |
45 | - computed(state.times, times => { | |
46 | - return times | |
47 | - .map(time => time.date) | |
48 | - // .sort((a, b) => a - b) | |
49 | - .map(TimeEntry) | |
50 | - }), | |
51 | - NewTimeEntry() | |
52 | - ]), | |
45 … | + TimePicker(state), | |
53 | 46 … | h('div.timezone', [ |
54 | 47 … | h('label', 'Timezone of your scry is'), |
55 | 48 … | h('div.zone', [ |
56 | 49 … | getTimezone(), |
@@ -66,62 +59,8 @@ | ||
66 | 59 … | function setMonth (d) { |
67 | 60 … | state.monthIndex.set(state.monthIndex() + d) |
68 | 61 … | } |
69 | 62 … | |
70 | - function NewTimeEntry () { | |
71 | - var active = Value(false) | |
72 | - | |
73 | - // TODO extract and only run once | |
74 | - const options = Array(96).fill(0).map((_, i) => { | |
75 | - var time = new Date () | |
76 | - time.setHours(0) | |
77 | - time.setMinutes(15 * i) | |
78 | - return time | |
79 | - }) | |
80 | - const DAY_START_SELECTOR = 'day-start' | |
81 | - | |
82 | - const el = h('div.new-time-entry', [ | |
83 | - when(active, | |
84 | - h('div.dropdown', options.map((time, i) => { | |
85 | - return h('div', | |
86 | - { | |
87 | - 'ev-click': () => { | |
88 | - state.times.push(Event(time)) | |
89 | - active.set(false) | |
90 | - }, | |
91 | - className: i === 32 ? DAY_START_SELECTOR : '' | |
92 | - }, | |
93 | - printTime(time) | |
94 | - ) | |
95 | - })) | |
96 | - ), | |
97 | - h('div.add', { 'ev-click': activate }, '+ Add times') | |
98 | - ]) | |
99 | - | |
100 | - return el | |
101 | - | |
102 | - function activate () { | |
103 | - active.set(true) | |
104 | - | |
105 | - const target = el.querySelector('.' + DAY_START_SELECTOR) | |
106 | - target.parentNode.scrollTop = target.offsetTop - target.parentNode.offsetTop + 4 | |
107 | - } | |
108 | - } | |
109 | - | |
110 | - function TimeEntry (date) { | |
111 | - return h('div.time-entry', [ | |
112 | - h('div.time', printTime(date)), | |
113 | - h('div.close', { 'ev-click': () => removeTime(date) }, '×') | |
114 | - // h('i.fa.fa-close') | |
115 | - ]) | |
116 | - } | |
117 | - | |
118 | - function removeTime (d) { | |
119 | - var ev = state.times.find(time => time.date === d) | |
120 | - | |
121 | - if (ev) state.times.delete(ev) | |
122 | - } | |
123 | - | |
124 | 63 … | function MonthTitle (monthIndex) { |
125 | 64 … | const MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] |
126 | 65 … | |
127 | 66 … | return computed(monthIndex, mi => { |
@@ -169,14 +108,4 @@ | ||
169 | 108 … | function getTimezoneOffset () { |
170 | 109 … | const offset = new Date().getTimezoneOffset() / -60 |
171 | 110 … | return offset > 0 ? `+${offset}` : offset |
172 | 111 … | } |
173 | - | |
174 | -function printTime (date) { | |
175 | - var hours = date.getHours().toString() | |
176 | - while (hours.length < 2) hours = `0${hours}` | |
177 | - | |
178 | - var minutes = date.getMinutes().toString() | |
179 | - while (minutes.length < 2) minutes = `0${minutes}` | |
180 | - | |
181 | - return `${hours}:${minutes}` | |
182 | -} |
views/new.mcss | ||
---|---|---|
@@ -64,59 +64,9 @@ | ||
64 | 64 … | |
65 | 65 … | margin: var(--boundary) var(--boundary) calc(var(--boundary) / 2) var(--boundary) |
66 | 66 … | } |
67 | 67 … | |
68 | - div.picker { | |
69 | - color: #666 | |
70 | - background: #fff | |
71 | - border-radius: var(--br) | |
72 | - margin: 0 var(--boundary) var(--boundary) | |
73 | 68 … | |
74 | - div.time-entry { | |
75 | - padding: calc(var(--boundary) / 2) | |
76 | - border-bottom: 1px solid hsla(0, 0%, 0%, .2) | |
77 | - | |
78 | - display: flex | |
79 | - justify-content: space-between | |
80 | - | |
81 | - div.close { cursor: pointer } | |
82 | - } | |
83 | - | |
84 | - div.new-time-entry { | |
85 | - font-size: .9rem | |
86 | - position: relative | |
87 | - | |
88 | - div.dropdown { | |
89 | - position: absolute | |
90 | - left: calc(var(--boundary) / 2) | |
91 | - right: calc(var(--boundary) / 2) | |
92 | - top: calc(var(--boundary) / 2) | |
93 | - | |
94 | - background: #fff | |
95 | - max-height: 20vh | |
96 | - overflow-y: scroll | |
97 | - padding: calc(var(--boundary) / 2) | |
98 | - | |
99 | - div { | |
100 | - padding: 2px | |
101 | - :hover { | |
102 | - background: var(--feature-color) | |
103 | - color: #fff | |
104 | - } | |
105 | - } | |
106 | - } | |
107 | - | |
108 | - div.add { | |
109 | - color: var(--feature-color) | |
110 | - padding: calc(var(--boundary) / 2) | |
111 | - cursor: pointer | |
112 | - | |
113 | - display: flex | |
114 | - justify-content: center | |
115 | - } | |
116 | - } | |
117 | - } | |
118 | - | |
119 | 69 … | div.timezone { |
120 | 70 … | padding: var(--boundary) |
121 | 71 … | border-top: hsla(0, 0%, 100%, .5) 1px dashed |
122 | 72 … |
views/component/time-picker.js | ||
---|---|---|
@@ -1,0 +1,83 @@ | ||
1 … | +const { h, computed, Value, when } = require('mutant') | |
2 … | + | |
3 … | +module.exports = function TimePicker ({ times }) { | |
4 … | + return h('ScryTimePicker', [ | |
5 … | + computed(times, times => { | |
6 … | + return times | |
7 … | + .map(time => time.date) | |
8 … | + // .sort((a, b) => a - b) | |
9 … | + .map(t => TimeEntry(t, times)) | |
10 … | + }), | |
11 … | + NewTimeEntry(times) | |
12 … | + ]) | |
13 … | +} | |
14 … | + | |
15 … | +function NewTimeEntry (times) { | |
16 … | + var active = Value(false) | |
17 … | + | |
18 … | + const options = Array(96).fill(0).map((_, i) => { | |
19 … | + var time = new Date () | |
20 … | + time.setHours(0) | |
21 … | + time.setMinutes(15 * i) | |
22 … | + return time | |
23 … | + }) | |
24 … | + const DAY_START_SELECTOR = 'day-start' | |
25 … | + | |
26 … | + const el = h('div.add-more', [ | |
27 … | + when(active, | |
28 … | + h('div.dropdown', options.map((time, i) => { | |
29 … | + return h('div', | |
30 … | + { | |
31 … | + 'ev-click': () => { | |
32 … | + times.push(Event(time)) | |
33 … | + active.set(false) | |
34 … | + }, | |
35 … | + className: i === 32 ? DAY_START_SELECTOR : '' | |
36 … | + }, | |
37 … | + printTime(time) | |
38 … | + ) | |
39 … | + })) | |
40 … | + ), | |
41 … | + h('div.add', { 'ev-click': activate }, '+ Add times') | |
42 … | + ]) | |
43 … | + | |
44 … | + return el | |
45 … | + | |
46 … | + function activate () { | |
47 … | + active.set(true) | |
48 … | + | |
49 … | + const target = el.querySelector('.' + DAY_START_SELECTOR) | |
50 … | + target.parentNode.scrollTop = target.offsetTop - target.parentNode.offsetTop + 4 | |
51 … | + } | |
52 … | +} | |
53 … | + | |
54 … | +function TimeEntry (t, times) { | |
55 … | + return h('div.time-entry', [ | |
56 … | + h('div.time', printTime(t)), | |
57 … | + h('div.close', { 'ev-click': () => removeTime(t, times) }, '×') | |
58 … | + // h('i.fa.fa-close') | |
59 … | + ]) | |
60 … | +} | |
61 … | + | |
62 … | +function printTime (date) { | |
63 … | + var hours = date.getHours().toString() | |
64 … | + while (hours.length < 2) hours = `0${hours}` | |
65 … | + | |
66 … | + var minutes = date.getMinutes().toString() | |
67 … | + while (minutes.length < 2) minutes = `0${minutes}` | |
68 … | + | |
69 … | + return `${hours}:${minutes}` | |
70 … | +} | |
71 … | + | |
72 … | +function removeTime (t, times) { | |
73 … | + var ev = times.find(time => time.date === t) | |
74 … | + | |
75 … | + if (ev) times.delete(ev) | |
76 … | +} | |
77 … | + | |
78 … | +function Event (date) { | |
79 … | + return { | |
80 … | + date, | |
81 … | + data: {attending: true} | |
82 … | + } | |
83 … | +} |
views/component/time-picker.mcss | ||
---|---|---|
@@ -1,0 +1,51 @@ | ||
1 … | +ScryTimePicker { | |
2 … | + color: #666 | |
3 … | + background: #fff | |
4 … | + border-radius: var(--br) | |
5 … | + margin: 0 var(--boundary) var(--boundary) | |
6 … | + | |
7 … | + div.time-entry { | |
8 … | + padding: calc(var(--boundary) / 2) | |
9 … | + border-bottom: 1px solid hsla(0, 0%, 0%, .2) | |
10 … | + | |
11 … | + display: flex | |
12 … | + justify-content: space-between | |
13 … | + | |
14 … | + div.close { cursor: pointer } | |
15 … | + } | |
16 … | + | |
17 … | + div.add-more { | |
18 … | + font-size: .9rem | |
19 … | + position: relative | |
20 … | + | |
21 … | + div.dropdown { | |
22 … | + position: absolute | |
23 … | + left: calc(var(--boundary) / 2) | |
24 … | + right: calc(var(--boundary) / 2) | |
25 … | + top: calc(var(--boundary) / 2) | |
26 … | + | |
27 … | + background: #fff | |
28 … | + max-height: 20vh | |
29 … | + overflow-y: scroll | |
30 … | + padding: calc(var(--boundary) / 2) | |
31 … | + | |
32 … | + div { | |
33 … | + padding: 2px | |
34 … | + :hover { | |
35 … | + background: var(--feature-color) | |
36 … | + color: #fff | |
37 … | + } | |
38 … | + } | |
39 … | + } | |
40 … | + | |
41 … | + div.add { | |
42 … | + color: var(--feature-color) | |
43 … | + padding: calc(var(--boundary) / 2) | |
44 … | + cursor: pointer | |
45 … | + | |
46 … | + display: flex | |
47 … | + justify-content: center | |
48 … | + } | |
49 … | + } | |
50 … | +} | |
51 … | + |
Built with git-ssb-web