From 7064f2b1479f71b104a049b837d51aafbcd36aa9 Mon Sep 17 00:00:00 2001 From: Adam Procter <adamprocter@researchnot.es> Date: Tue, 15 Dec 2020 14:59:37 +0000 Subject: [PATCH] draggable cards p1 --- app/package-lock.json | 13 ++ app/package.json | 1 + app/src/components/CardsLayer.vue | 200 ++++++++++++++++++++- app/src/components/CardsLayerOld.vue | 235 +++++++++++++++++++++++++ app/src/components/OtherCardslayer.vue | 125 ++++++------- app/src/views/Cards.vue | 3 +- 6 files changed, 515 insertions(+), 62 deletions(-) create mode 100644 app/src/components/CardsLayerOld.vue diff --git a/app/package-lock.json b/app/package-lock.json index 170b90b..22e0072 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -16099,6 +16099,11 @@ "is-plain-obj": "^1.0.0" } }, + "sortablejs": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz", + "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==" + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -17651,6 +17656,14 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuedraggable": { + "version": "2.24.3", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz", + "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==", + "requires": { + "sortablejs": "1.10.2" + } + }, "vuex": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz", diff --git a/app/package.json b/app/package.json index 50e714c..987a96f 100644 --- a/app/package.json +++ b/app/package.json @@ -19,6 +19,7 @@ "vue-emoji-picker": "^1.0.1", "vue-router": "^3.4.9", "vue-swatches": "^2.1.0", + "vuedraggable": "^2.24.3", "vuex": "^3.6.0" }, "devDependencies": { diff --git a/app/src/components/CardsLayer.vue b/app/src/components/CardsLayer.vue index 9deae18..a51c8f8 100644 --- a/app/src/components/CardsLayer.vue +++ b/app/src/components/CardsLayer.vue @@ -1,5 +1,5 @@ <template> - <div class="grid"> + <draggable @start="drag = true" @end="drag = false"> <div v-for="(nodes, index) in $options.myArray" v-bind:key="index"> <form class="nodes"> <template v-if="nodes.read_mode == false"> @@ -46,15 +46,95 @@ </div> </form> </div> - </div> + + <div + v-for="(othernodes, index) in othernodes_filtered" + v-bind:key="index.nodeid" + > + <div class="nodes"> + <p :inner-html.prop="othernodes.node_text | marked"></p> + + <div class="eeee"> + <input :value="othernodes.node_id" name="id" readonly hidden /> + <input + id="emojifield" + class="regular-input" + v-model="input" + readonly + /> + + <emoji-picker @emoji="append" :search="search"> + <div + class="emoji-invoker" + slot="emoji-invoker" + slot-scope="{ events: { click: clickEvent } }" + @click.stop="clickEvent" + > + <svg + height="24" + viewBox="0 0 24 24" + width="24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M0 0h24v24H0z" fill="none" /> + <path + d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" + /> + </svg> + </div> + <div slot="emoji-picker" slot-scope="{ emojis, insert, display }"> + <div + class="emoji-picker" + :style="{ top: display.y + 'px', left: display.x + 'px' }" + > + <div class="emoji-picker__search"> + <input type="text" v-model="search" /> + </div> + <div> + <div v-for="(emojiGroup, category) in emojis" :key="category"> + <h5>{{ category }}</h5> + <div class="emojis"> + <span + v-for="(emoji, emojiName) in emojiGroup" + :key="emojiName" + @click="insert(emoji), sentReact(othernodes.node_id)" + :title="emojiName" + >{{ emoji }}</span + > + </div> + </div> + </div> + </div> + </div> + </emoji-picker> + + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="othernodes.node_id == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> + </div> + </div> + </div> + </div> + </draggable> </template> <script> import { mapState } from 'vuex' import marked from 'marked' +import draggable from 'vuedraggable' +import EmojiPicker from 'vue-emoji-picker' import SvgButton from '@/components/SvgButton' import SvgButton2 from '@/components/SvgButton2' var readmode +var nodeid +var emojitext export default { name: 'CardsLayer', @@ -67,6 +147,8 @@ export default { localreadmode: false, myArray: null, update: false, + input: '', + search: '', } }, @@ -75,6 +157,7 @@ export default { }, computed: { ...mapState({ + otherNodes: (state) => state.otherNodes, myNodes: (state) => state.myNodes, configPositions: (state) => state.configPositions, configEmoji: (state) => state.configEmoji, @@ -85,6 +168,11 @@ export default { return nodes.deleted == false }) }, + othernodes_filtered: function () { + return this.otherNodes.filter((othernodes) => { + return othernodes.deleted == false + }) + }, }, // this is to stop sync chasing bug @@ -123,11 +211,26 @@ export default { }, methods: { + append(emoji) { + this.input += emoji + }, + sentReact(e) { + emojitext = this.input + nodeid = e + + this.$store.dispatch('addEmoji', { + nodeid, + emojitext, + }) + + this.input = '' + }, updateNodes() { this.update = !this.update }, loadData() { this.$options.myArray = this.nodes_filtered + this.$forceUpdate() }, editNode(e) { @@ -162,6 +265,8 @@ export default { components: { SvgButton, SvgButton2, + EmojiPicker, + draggable, }, } </script> @@ -224,6 +329,97 @@ textarea { margin: 0em; } +input { + display: none; +} + +.allemoji { + font-size: 2em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(0, auto)); + + /* float: left; */ +} + +.eachemoji p { + margin: 0em; +} + +.emoji-invoker { + top: -0.5rem; + right: 0.5rem; + width: 1.5rem; + height: 2.5rem; + /* transform: scale(1.6); */ + /* margin: 0em 0em 1em 0em; */ + /* border-radius: 50%; */ + cursor: pointer; + transition: all 0.8s; +} +.emoji-invoker:hover > svg { + fill: #84949b; + + /* transform: scale(1.5); */ +} +.emoji-invoker > svg { + fill: #b1c6d0; + margin-top: 10px; + margin-left: 0.2em; + transform: scale(1.5); +} + +.emoji-picker { + transform: scale(1.2); + z-index: 1; + font-family: Montserrat; + border: 1px solid #ccc; + width: 18rem; + height: 20rem; + overflow: scroll; + padding: 1rem; + box-sizing: border-box; + border-radius: 0.5rem; + background: #fff; + box-shadow: 1px 1px 8px #c7dbe6; + margin-top: 3em; +} +.emoji-picker__search { + display: flex; +} +.emoji-picker__search > input { + flex: 1; + border-radius: 10rem; + border: 1px solid #ccc; + padding: 0.5rem 1rem; + outline: none; +} +.emoji-picker h5 { + margin-top: 0; + margin-bottom: 0; + color: #b1b1b1; + text-transform: uppercase; + font-size: 0.8rem; + cursor: default; +} +.emoji-picker .emojis { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +.emoji-picker .emojis:after { + content: ''; + flex: auto; +} +.emoji-picker .emojis span { + padding: 0.2rem; + cursor: pointer; + border-radius: 5px; +} +.emoji-picker .emojis span:hover { + background: #ececec; + cursor: pointer; +} + @media only screen and (max-width: 600px) { .readmode >>> a { font-size: 2em; diff --git a/app/src/components/CardsLayerOld.vue b/app/src/components/CardsLayerOld.vue new file mode 100644 index 0000000..df9bd25 --- /dev/null +++ b/app/src/components/CardsLayerOld.vue @@ -0,0 +1,235 @@ +<template> + <div class="grid"> + <div v-for="(nodes, index) in $options.myArray" v-bind:key="index"> + <form class="nodes"> + <template v-if="nodes.read_mode == false"> + <textarea + @focus="editTrue(true)" + @blur="editTrue(false)" + autofocus + v-model="nodes.node_text" + @input="editNode" + :id="nodes.node_id" + ref="nodetext" + placeholder="Type your thoughts and ideas here! (auto saved every keystroke)" + ></textarea> + </template> + <template v-else> + <p + :id="nodes.node_id" + :inner-html.prop="nodes.node_text | marked" + ></p> + </template> + <div class="btn-row"> + <SvgButton + buttonClass="nodes" + @click.prevent="deleteFlag(nodes.node_id), updateNodes()" + /> + <SvgButton2 + buttonClass="nodes" + @click.prevent=" + readFlag(nodes.node_id, nodes.read_mode), updateNodes() + " + /> + </div> + + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <template v-if="emojis.node_id == nodes.node_id">{{ + emojis.emoji_text + }}</template> + </div> + </div> + </form> + </div> + </div> +</template> + +<script> +import { mapState } from 'vuex' +import marked from 'marked' + +import SvgButton from '@/components/SvgButton' +import SvgButton2 from '@/components/SvgButton2' +var readmode + +export default { + name: 'CardsLayer', + + props: { + added: Boolean, + }, + data: function () { + return { + localreadmode: false, + myArray: null, + update: false, + } + }, + + filters: { + marked: marked, + }, + computed: { + ...mapState({ + myNodes: (state) => state.myNodes, + configPositions: (state) => state.configPositions, + configEmoji: (state) => state.configEmoji, + }), + + nodes_filtered: function () { + return this.myNodes.filter((nodes) => { + return nodes.deleted == false + }) + }, + }, + + // this is to stop sync chasing bug + + mounted() { + //access the custom option using $options + const unwatch = this.$watch('nodes_filtered', (value) => { + this.$options.myArray = this.nodes_filtered + + this.$forceUpdate() + // ignore falsy values + if (!value) return + + // stop watching when nodes_filtered[] is not empty + if (value && value.length) unwatch() + + // process value here + }) + }, + + watch: { + added: { + deep: true, + + handler() { + setTimeout(this.loadData, 200) + }, + }, + update: { + deep: true, + + handler() { + setTimeout(this.loadData, 200) + }, + }, + }, + + methods: { + updateNodes() { + this.update = !this.update + }, + loadData() { + this.$options.myArray = this.nodes_filtered + this.$forceUpdate() + }, + editNode(e) { + var nodeid = e.target.id + var nodetext = e.target.value + this.$store.dispatch('editNode', { nodeid, nodetext }) + }, + + editTrue(e) { + this.$emit('edit-true', e) + }, + + deleteFlag(e) { + if (confirm('Confirm discard?')) { + this.$store.dispatch('deleteFlag', { e }) + } else { + // nothing happens + } + }, + readFlag(e, f) { + readmode = f + readmode = !readmode + this.$store.dispatch('readFlag', { e, readmode }) + + if (readmode == true) { + this.mode = 'Read' + } else { + this.mode = 'Edit' + } + }, + }, + components: { + SvgButton, + SvgButton2, + }, +} +</script> + +<style lang="css" scoped> +.grid { + display: flex; + flex-wrap: wrap; +} +.nodes { + min-width: 343px; + max-width: 343px; + border: 2px dashed black; + background-color: rgb(155, 194, 216); + margin-top: 1em; + margin-left: 0.5em; + margin-right: 0.5em; +} + +.readmode { + margin-top: 1em; + margin-left: 1em; + padding-right: 1em; +} + +textarea { + width: 100%; + height: 175px; + resize: none; + font-size: 18px; + box-sizing: border-box; + font-family: 'Inter var', Helvetica, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + border: none; + outline: none; + background-color: rgb(187, 227, 255); + scrollbar-color: yellow rgb(187, 227, 255); +} +.btn-row { + position: relative; + margin-bottom: 5px; + display: flex; + justify-content: center; + flex-wrap: wrap; + padding: 0 15px; + border-radius: 4px; +} + +.allemoji { + font-size: 2em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(0, auto)); + + /* float: left; */ +} + +.eachemoji p { + margin: 0em; +} + +@media only screen and (max-width: 600px) { + .readmode >>> a { + font-size: 2em; + word-break: break-all; + padding-right: 0.5em; + } +} +</style> diff --git a/app/src/components/OtherCardslayer.vue b/app/src/components/OtherCardslayer.vue index 711d1bc..36b2859 100644 --- a/app/src/components/OtherCardslayer.vue +++ b/app/src/components/OtherCardslayer.vue @@ -1,82 +1,88 @@ <template> <div class="grid"> - <div v-for="(nodes, index) in othernodes_filtered" v-bind:key="index"> - <div class="nodes"> - <p :inner-html.prop="nodes.node_text | marked"></p> + + <div v-for="(nodes, index) in othernodes_filtered" v-bind:key="index"> + <div class="nodes"> + <p :inner-html.prop="nodes.node_text | marked"></p> - <div class="eeee"> - <input :value="nodes.node_id" name="id" readonly hidden /> - <input - id="emojifield" - class="regular-input" - v-model="input" - readonly - /> + <div class="eeee"> + <input :value="nodes.node_id" name="id" readonly hidden /> + <input + id="emojifield" + class="regular-input" + v-model="input" + readonly + /> - <emoji-picker @emoji="append" :search="search"> - <div - class="emoji-invoker" - slot="emoji-invoker" - slot-scope="{ events: { click: clickEvent } }" - @click.stop="clickEvent" - > - <svg - height="24" - viewBox="0 0 24 24" - width="24" - xmlns="http://www.w3.org/2000/svg" - > - <path d="M0 0h24v24H0z" fill="none" /> - <path - d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" - /> - </svg> - </div> - <div slot="emoji-picker" slot-scope="{ emojis, insert, display }"> + <emoji-picker @emoji="append" :search="search"> <div - class="emoji-picker" - :style="{ top: display.y + 'px', left: display.x + 'px' }" + class="emoji-invoker" + slot="emoji-invoker" + slot-scope="{ events: { click: clickEvent } }" + @click.stop="clickEvent" > - <div class="emoji-picker__search"> - <input type="text" v-model="search" /> - </div> - <div> - <div v-for="(emojiGroup, category) in emojis" :key="category"> - <h5>{{ category }}</h5> - <div class="emojis"> - <span - v-for="(emoji, emojiName) in emojiGroup" - :key="emojiName" - @click="insert(emoji), sentReact(nodes.node_id)" - :title="emojiName" - >{{ emoji }}</span - > + <svg + height="24" + viewBox="0 0 24 24" + width="24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M0 0h24v24H0z" fill="none" /> + <path + d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" + /> + </svg> + </div> + <div slot="emoji-picker" slot-scope="{ emojis, insert, display }"> + <div + class="emoji-picker" + :style="{ top: display.y + 'px', left: display.x + 'px' }" + > + <div class="emoji-picker__search"> + <input type="text" v-model="search" /> + </div> + <div> + <div + v-for="(emojiGroup, category) in emojis" + :key="category" + > + <h5>{{ category }}</h5> + <div class="emojis"> + <span + v-for="(emoji, emojiName) in emojiGroup" + :key="emojiName" + @click="insert(emoji), sentReact(nodes.node_id)" + :title="emojiName" + >{{ emoji }}</span + > + </div> </div> </div> </div> </div> - </div> - </emoji-picker> + </emoji-picker> - <div class="allemoji"> - <div - class="eachemoji" - v-for="(emojis, index) in configEmoji" - :key="index" - > - <p v-if="nodes.node_id == emojis.node_id"> - {{ emojis.emoji_text }} - </p> + <div class="allemoji"> + <div + class="eachemoji" + v-for="(emojis, index) in configEmoji" + :key="index" + > + <p v-if="nodes.node_id == emojis.node_id"> + {{ emojis.emoji_text }} + </p> + </div> </div> </div> </div> </div> - </div> + </div> </template> <script> import { mapState } from 'vuex' + import EmojiPicker from 'vue-emoji-picker' import marked from 'marked' @@ -88,6 +94,7 @@ export default { components: { EmojiPicker, + }, data: function () { diff --git a/app/src/views/Cards.vue b/app/src/views/Cards.vue index f4b478f..622faae 100644 --- a/app/src/views/Cards.vue +++ b/app/src/views/Cards.vue @@ -5,7 +5,7 @@ <CardsLayer @edit-true="(e) => editTrue(e)" :added="added" /> - <OtherCardslayer /> + <!-- <OtherCardslayer /> --> <!-- <BaseButton class="new" buttonClass="action" @click="addNode()" >Create Node</BaseButton @@ -58,6 +58,7 @@ import CardsLayer from '@/components/CardsLayer' import OtherCardslayer from '@/components/OtherCardslayer' import OnBoard from '@/components/OnBoard' import UploadLayer from '@/components/UploadLayer' + import { mapState } from 'vuex' import { shortcutsMixin } from '@/components/mixins/shortcutsMixin.js' -- GitLab