-
-
Notifications
You must be signed in to change notification settings - Fork 398
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hashtags Feature applied to Proposals (#3959)
* initial commit working on hashtags system * applying hashtags on proposals * applied requested changes * fix specs * change eslint in package json * fix input_hashtags.js * change versions package.json * fix rubocop offenses * Don't leave trailing whitespace * remove hashtaggable on meeting * add changelog lines
- Loading branch information
Showing
59 changed files
with
734 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
decidim-core/app/assets/javascripts/decidim/input_hashtags.js.es6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// = require tribute | ||
|
||
$(() => { | ||
const $hashtagContainer = $(".js-hashtags"); | ||
const nodatafound = $hashtagContainer.attr("data-noresults"); | ||
|
||
// Listener for the event triggered by quilljs | ||
let cursor = ""; | ||
$hashtagContainer.on("quill-position", function(event) { | ||
if (event.detail !== null) { | ||
cursor = event.detail.index; | ||
} | ||
}); | ||
|
||
/* eslint no-use-before-define: ["error", { "variables": false }]*/ | ||
let remoteSearch = function(text, cb) { | ||
$.post("/api", {query: `{hashtags(name:${text}) {name}}`}). | ||
|
||
then((response) => { | ||
let data = response.data.hashtags || {}; | ||
cb(data) | ||
}).fail(function() { | ||
cb([]) | ||
}).always(() => { | ||
// This function runs Tribute every single time you type something | ||
// So we must evalute DOM properties after each | ||
const $parent = $(tribute.current.element).parent() | ||
$parent.addClass("is-active") | ||
|
||
// We need to move the container to the wrapper selected | ||
const $tribute = $parent.find(".tribute-container"); | ||
// Remove the inline styles, relative to absolute positioning | ||
$tribute.removeAttr("style"); | ||
}) | ||
}; | ||
|
||
// tribute.js docs - http://github.com/zurb/tribute | ||
/* global Tribute*/ | ||
let tribute = new Tribute({ | ||
trigger: "#", | ||
values: function (text, cb) { | ||
remoteSearch(text, (hashtags) => cb(hashtags)); | ||
}, | ||
positionMenu: true, | ||
menuContainer: null, | ||
fillAttr: "name", | ||
noMatchTemplate: () => `<li>${nodatafound}</li>`, | ||
lookup: (item) => item.name, | ||
selectTemplate: function(item) { | ||
if (typeof item === "undefined") { | ||
return null; | ||
} | ||
if (this.range.isContentEditable(this.current.element)) { | ||
// Check quill.js | ||
if ($(this.current.element).hasClass("editor-container")) { | ||
let quill = this.current.element.__quill; | ||
quill.insertText(cursor - 1, `#${item.original.name} `, Quill.sources.API); | ||
// cursor position + hashtag length + "#" sign + space | ||
let position = cursor + item.original.name.length + 2; | ||
|
||
let next = 0; | ||
if (quill.getLength() > position) { | ||
next = position | ||
} else { | ||
next = quill.getLength() - 1 | ||
} | ||
// Workaround https://github.com/quilljs/quill/issues/731 | ||
setTimeout(function () { | ||
quill.setSelection(next, 0); | ||
}, 500); | ||
|
||
return "" | ||
} | ||
return `<span contenteditable="false">#${item.original.name}</span>`; | ||
} | ||
return `#${item.original.name}`; | ||
}, | ||
menuItemTemplate: function(item) { | ||
let tpl = `<strong>${item.original.name}</strong>`; | ||
return tpl; | ||
} | ||
}); | ||
|
||
tribute.attach($hashtagContainer); | ||
|
||
// DOM manipulation | ||
$hashtagContainer.on("focusin", (event) => { | ||
// Set the parent container relative to the current element | ||
|
||
tribute.menuContainer = event.target.parentNode; | ||
}); | ||
$hashtagContainer.on("focusout", (event) => { | ||
let $parent = $(event.target).parent(); | ||
|
||
if ($parent.hasClass("is-active")) { | ||
$parent.removeClass("is-active"); | ||
} | ||
}); | ||
$hashtagContainer.on("input", (event) => { | ||
let $parent = $(event.target).parent(); | ||
$parent.removeAttr("style"); | ||
|
||
if (tribute.isActive) { | ||
// We need to move the container to the wrapper selected | ||
let $tribute = $(".tribute-container"); | ||
$tribute.removeAttr("style"); | ||
$tribute.appendTo($parent); | ||
// Remove the inline styles, relative to absolute positioning | ||
// Parent adaptation | ||
$parent.addClass("is-active"); | ||
} else { | ||
$parent.removeClass("is-active"); | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
decidim-core/app/assets/stylesheets/decidim/modules/_input-hashtags.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
@import "tribute"; | ||
|
||
.hashtags__container{ | ||
width: 100%; | ||
|
||
span{ | ||
color: $secondary; | ||
} | ||
|
||
[type=text]{ | ||
white-space: nowrap; | ||
overflow: hidden; | ||
|
||
br{ | ||
display: none; | ||
} | ||
|
||
*{ | ||
display: inline; | ||
white-space: nowrap; | ||
} | ||
} | ||
|
||
[contenteditable=true]:empty::before{ | ||
color: $input-placeholder-color; | ||
content: attr(placeholder); | ||
display: block; // firefox | ||
} | ||
|
||
&.hashtags__container--textarea{ | ||
[contenteditable=true]{ | ||
-webkit-appearance: textfield; | ||
-moz-appearance: textfield-multiline; | ||
appearance: textfield; | ||
|
||
// foundation defaults | ||
@include form-element(); | ||
|
||
max-width: 100%; | ||
// Min size, 3 times input textfield | ||
min-height: 3 * (($input-font-size * unitless-calc($input-line-height)) + (get-side($input-padding, 'top') + get-side($input-padding, 'bottom'))); | ||
height: auto; | ||
|
||
// Placeholder text | ||
&::placeholder{ | ||
color: $input-placeholder-color; | ||
} | ||
|
||
// Disabled/readonly state | ||
&:disabled, | ||
&[readonly]{ | ||
background-color: $input-background-disabled; | ||
cursor: $input-cursor-disabled; | ||
} | ||
} | ||
} | ||
|
||
// Override defaults | ||
.tribute-container{ | ||
position: relative; | ||
min-width: 100%; | ||
top: -1rem - rem-calc(1); | ||
|
||
ul{ | ||
//reset list | ||
margin: 0; | ||
list-style: none; | ||
// input defaults | ||
outline: none; | ||
border: $input-border-focus; | ||
border-radius: $input-radius; | ||
background-color: $input-background-focus; | ||
box-shadow: $input-shadow-focus; | ||
|
||
@if has-value($input-transition){ | ||
transition: $input-transition; | ||
} | ||
|
||
li{ | ||
padding: $input-padding; | ||
border-top: $border; | ||
} | ||
} | ||
|
||
.highlight{ | ||
background-color: $input-prefix-background; | ||
} | ||
} | ||
|
||
&.is-active{ | ||
[contenteditable=true], | ||
input, | ||
textarea{ | ||
border-bottom-left-radius: 0; | ||
border-bottom-right-radius: 0; | ||
} | ||
|
||
.tribute-container{ | ||
ul{ | ||
border-top: 0; | ||
border-top-left-radius: 0; | ||
border-top-right-radius: 0; | ||
} | ||
} | ||
|
||
// Quill.js | ||
&.editor{ | ||
> :first-child{ | ||
border-top-left-radius: $input-radius; | ||
border-top-right-radius: $input-radius; | ||
border-top: $input-border-focus; | ||
border-right: $input-border-focus; | ||
border-left: $input-border-focus; | ||
} | ||
|
||
.editor-container{ | ||
border-right: $input-border-focus; | ||
border-left: $input-border-focus; | ||
margin-bottom: 1rem; | ||
outline: none; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.