koro

an event time scheduler
git clone https://tilde.team/~marisa/repo/koro.git
Log | Files | Refs | README | LICENSE

commit 5044b14d66f071b15f4c918b07b34d069b32432b
parent 4db7eb5818466e43f567e99f142da27888aac7aa
Author: mokou <mokou@posteo.de>
Date:   Thu, 21 May 2020 00:03:18 +0200

feat: Design for the creation page

Diffstat:
Mpackage-lock.json | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackage.json | 2++
Asrc/components/DateTimeGrid.svelte | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.css | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/pages/Index.svelte | 38++++++++++++++++++++++++++++++++++++++
Mtailwind.config.js | 5++++-
6 files changed, 322 insertions(+), 1 deletion(-)

diff --git a/package-lock.json b/package-lock.json @@ -263,6 +263,11 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, + "atoa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", + "integrity": "sha1-DMDpGkgOc4+SPrwQNnZHF3mzSkk=" + }, "autoprefixer": { "version": "9.8.0", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.0.tgz", @@ -345,6 +350,16 @@ "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", "dev": true }, + "bullseye": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bullseye/-/bullseye-1.5.0.tgz", + "integrity": "sha1-gaZ4ZYGUjfR7sdaQvb9IRMuGC4w=", + "requires": { + "crossvent": "^1.3.1", + "seleccion": "2.0.0", + "sell": "^1.0.0" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -525,6 +540,15 @@ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, + "contra": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", + "integrity": "sha1-9TveQtfltZhcrk2ZqNYQUm3o8o0=", + "requires": { + "atoa": "1.0.0", + "ticky": "1.0.1" + } + }, "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -558,6 +582,14 @@ } } }, + "crossvent": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz", + "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=", + "requires": { + "custom-event": "1.0.0" + } + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -810,6 +842,16 @@ } } }, + "custom-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", + "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=" + }, + "dayjs": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.27.tgz", + "integrity": "sha512-Jpa2acjWIeOkg8KURUHICk0EqnEFSSF5eMEscsOgyJ92ZukXwmpmRkPSUka7KHSfbj5eKH30ieosYip+ky9emQ==" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -1492,6 +1534,11 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "fuzzysearch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fuzzysearch/-/fuzzysearch-1.0.3.tgz", + "integrity": "sha1-3/yA9tawQiPyImqnndGUIxCW0Ag=" + }, "generic-names": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", @@ -1580,12 +1627,39 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=" + }, "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, + "horsey": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/horsey/-/horsey-4.2.2.tgz", + "integrity": "sha1-RtQYk34Fcbdz6rNkv2wGEb+nAjg=", + "requires": { + "bullseye": "1.5.0", + "contra": "1.9.4", + "crossvent": "1.5.4", + "fuzzysearch": "1.0.3", + "hash-sum": "1.0.2", + "lodash": "4.13.1", + "sektor": "1.1.4", + "sell": "1.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.13.1.tgz", + "integrity": "sha1-g+SxCRP0hJbU0W/sSlYK8u50S2g=" + } + } + }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -3822,6 +3896,21 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "sektor": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sektor/-/sektor-1.1.4.tgz", + "integrity": "sha1-TM7kQczyrP5GlYws5GhCbKfHYuY=" + }, + "seleccion": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/seleccion/-/seleccion-2.0.0.tgz", + "integrity": "sha1-CYSsHo31E+OLQaYI5lBC6DgeCnM=" + }, + "sell": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sell/-/sell-1.0.0.tgz", + "integrity": "sha1-O6yn5R943e6eIu6hrHR6Y2i9FjA=" + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -4331,6 +4420,11 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "ticky": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", + "integrity": "sha1-t8+nHnaPHJAAxJe5FRswlHxQ5G0=" + }, "timsort": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", diff --git a/package.json b/package.json @@ -24,6 +24,8 @@ "tailwindcss": "^1.4.6" }, "dependencies": { + "dayjs": "^1.8.27", + "horsey": "^4.2.2", "navaid": "^1.1.1", "pouchdb-browser": "^7.2.1", "sirv-cli": "^0.4.4" diff --git a/src/components/DateTimeGrid.svelte b/src/components/DateTimeGrid.svelte @@ -0,0 +1,124 @@ +<script> + import day from 'dayjs' + import relativeTime from 'dayjs/plugin/relativeTime' + day.extend(relativeTime) + + export let startDate + export let selected = [] + let day1 = null + let datesDay1 = [] + let day2 = null + let datesDay2 = [] + let day3 = null + let datesDay3 = [] + let view = [0, 5] + initDays(day(startDate)) + + function initDays (start) { + let first = start + if (start.date() === day(startDate).date()) { + day1 = day(startDate) + first = day1.add(30, 'minutes').startOf('hour') + } else { + day1 = start + } + datesDay1 = [first] + let indexDay1 = first + while (indexDay1.add(30, 'minutes').date() === day1.date()) { + indexDay1 = indexDay1.add(30, 'minutes') + datesDay1.push(indexDay1) + } + day2 = day1.add(1, 'days').startOf('day') + datesDay2 = [day2] + let indexDay2 = day2 + while (indexDay2.add(30, 'minutes').date() === day2.date()) { + indexDay2 = indexDay2.add(30, 'minutes') + datesDay2.push(indexDay2) + } + day3 = day2.add(1, 'days').startOf('day') + datesDay3 = [day3] + let indexDay3 = day3 + while (indexDay3.add(30, 'minutes').date() === day3.date()) { + indexDay3 = indexDay3.add(30, 'minutes') + datesDay3.push(indexDay3) + } + } + + function previousDay () { + if (day1.date() !== day(startDate).date()) { + initDays(day1.subtract(1, 'day').startOf('day')) + } + } + + function addOrRemove (date) { + if (selected.includes(date.unix())) { + let c = selected + c.splice(selected.indexOf(date.unix()), 1) + selected = c + } else { + selected = [...selected, date.unix()] + } + console.log(selected) + } + + function nextDay () { + initDays(day1.add(1, 'day').startOf('day')) + } +</script> + +<div class="grid grid-cols-6 w-full datetimegrid"> + <div class="text-center hover:cursor-pointer" on:click={previousDay}> + {day1.date() === day(startDate).date() ? '' : '▲'} + </div> + <div class="text-center hover:cursor-pointer" on:click={() => view = view[0] !== 0 ? [view[0] - 1, view[1] - 1] : view}> + {view[0] !== 0 ? '‹' : ''} + </div> + <div class="col-span-3"></div> + <div class="text-center hover:cursor-pointer" on:click={() => view = [view[0] + 1, view[1] + 1]}>›</div> + <div class="font-bold">{day1.date() === day(startDate).date() ? 'today' : day1.endOf('day').fromNow()}</div> + {#if datesDay1.slice(view[0], view[1]).length < 5} + {#each new Array(5 - datesDay1.slice(view[0], view[1]).length) as a} + <div></div> + {/each} + {/if} + {#each datesDay1.slice(view[0], view[1]) as t} + <div + class="text-center time hover:cursor-pointer" + class:bg-purple-600={selected.includes(t.unix())} + class:text-white={selected.includes(t.unix())} + on:click={() => addOrRemove(t)} + >{t.format('hh:mm')}</div> + {/each} + + <div>{day2.date() === day(startDate).date() ? 'today' : day2.endOf('day').fromNow()}</div> + {#each datesDay2.slice(view[0], view[1]) as t} + <div + class="text-center time hover:cursor-pointer" + class:bg-purple-600={selected.includes(t.unix())} + class:text-white={selected.includes(t.unix())} + on:click={() => addOrRemove(t)} + >{t.format('hh:mm')}</div> + {/each} + + <div>{day3.date() === day(startDate).date() ? 'today' : day3.endOf('day').fromNow()}</div> + {#each datesDay3.slice(view[0], view[1]) as t} + <div + class="text-center time hover:cursor-pointer" + class:bg-purple-600={selected.includes(t.unix())} + class:text-white={selected.includes(t.unix())} + on:click={() => addOrRemove(t)} + >{t.format('hh:mm')}</div> + {/each} + <div class="text-center hover:cursor-pointer" on:click={nextDay}> + ▼ + </div> +</div> + +<div class="mt-2"> + {selected.length} potential time{selected.length === 1 ? '' : 's'} selected: + <ul class="list-disc"> + {#each selected as s} + <li><span class="font-bold">{day.unix(s).format('DD/MM/YYYY hh:mm')}</span> ({day.unix(s).fromNow()})</li> + {/each} + </ul> +</div> diff --git a/src/main.css b/src/main.css @@ -14,4 +14,64 @@ body { font-family: 'Inter'; } +input { + @apply block w-full px-5; + background-color: #fff; + border-color: #d2d6dc; + border-width: 1px; + border-radius: .375rem; + padding: .5rem .75rem; + font-size: 1rem; + line-height: 1.5; +} + +.btn { + @apply px-3 py-2 font-bold bg-purple-600 text-white border-purple-600 rounded-sm; + font-family: 'Inter'; +} + +.datetimegrid div { + @apply px-2 py-2 border transition duration-300; +} + +.datetimegrid div.time:hover { + @apply bg-purple-600 text-white; +} + +.sey-show { + @apply absolute bg-white border border-gray-600 w-64; +} + +.sey-show .sey-item:first-of-type { + @apply pt-2; +} + +.sey-show .sey-item { + @apply text-xl transition duration-300 px-3; +} + +.sey-show .sey-item:last-of-type { + @apply pb-2; +} + +.sey-show .sey-item:hover { + @apply cursor-pointer bg-purple-600 text-white; +} + +.sey-container:not(.sey-show) { + display: none; +} + +input:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(164, 202, 254, .45); + border-color: #a4cafe; +} + +@media sm { + input { + @apply text-sm leading-tight; + } +} + @import 'tailwindcss/utilities'; diff --git a/src/pages/Index.svelte b/src/pages/Index.svelte @@ -1,5 +1,34 @@ <script> + import horsey from 'horsey' + import { onMount } from 'svelte' + import DateTimeGrid from '../components/DateTimeGrid.svelte' + let durationInput = null + let durationsList = [ + '15 minutes', + '30 minutes', + '45 minutes', + '1 hour', + '2 hours', + '3 hours', + '4 hours', + '5 hours' + ] + let event = { + name: '', + duration: '', + times: [] + } + + async function submitForm () { + + } + + onMount(() => { + durationInput = horsey(document.querySelector('#duration-mount'), { + source: [{ list: durationsList }] + }) + }) </script> <h1 class="text-5xl font-serif font-extrabold">koro</h1> @@ -8,3 +37,12 @@ meeting, party, raid, whatever you want. It automatically supports the user's timezone and works locally, too. </p> + +<form class="max-w-xl mt-10" on:submit|preventDefault={submitForm}> + <label for="name" class="font-bold text-gray-600">Your event's name</label> + <input name="name" type="text" bind:value={event.name} placeholder="Raid with the boys" /> + <label for="duration" class="font-bold text-gray-600 mt-3">Duration of the event</label> + <input id="duration-mount" class="mb-5" bind:value={event.duration} name="duration-mount" placeholder="Start typing to see options..." /> + <DateTimeGrid startDate={new Date()} bind:selected={event.times} /> + <button class="btn mt-5" type="submit">Create event</button> +</form> diff --git a/tailwind.config.js b/tailwind.config.js @@ -2,5 +2,8 @@ module.exports = { purge: [ './src/**/*.svelte', './src/**/*.js' - ] + ], + variants: { + cursor: ['hover'] + } }