* Move tributejs to npm/webpack - Move vendored bundle to npm and webpack - Rewrote initialization to single function - Restyled it (made it a bit smaller) - Fixed it for arc-green * fix mention * also include emoji on #content * Update web_src/less/_tribute.less Co-authored-by: mrsdizzie <info@mrsdizzie.com> * rewrite to only use one instance of Tribute * refactor * fix copy/paste error Co-authored-by: mrsdizzie <info@mrsdizzie.com> Co-authored-by: techknowlogick <techknowlogick@gitea.io>tags/v1.13.0-rc1
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", | "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", | ||||
"integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" | "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" | ||||
}, | }, | ||||
"tributejs": { | |||||
"version": "5.1.3", | |||||
"resolved": "https://registry.npmjs.org/tributejs/-/tributejs-5.1.3.tgz", | |||||
"integrity": "sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==" | |||||
}, | |||||
"trim": { | "trim": { | ||||
"version": "0.0.1", | "version": "0.0.1", | ||||
"resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", | "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", |
"svgo-loader": "2.2.1", | "svgo-loader": "2.2.1", | ||||
"swagger-ui": "3.25.3", | "swagger-ui": "3.25.3", | ||||
"terser-webpack-plugin": "3.0.1", | "terser-webpack-plugin": "3.0.1", | ||||
"tributejs": "5.1.3", | |||||
"vue": "2.6.11", | "vue": "2.6.11", | ||||
"vue-bar-graph": "1.2.0", | "vue-bar-graph": "1.2.0", | ||||
"vue-calendar-heatmap": "0.8.4", | "vue-calendar-heatmap": "0.8.4", |
.tribute-container { | |||||
position: absolute; | |||||
top: 0; | |||||
left: 0; | |||||
height: auto; | |||||
max-height: 300px; | |||||
max-width: 500px; | |||||
overflow: auto; | |||||
display: block; | |||||
z-index: 999999; } | |||||
.tribute-container ul { | |||||
margin: 0; | |||||
margin-top: 2px; | |||||
padding: 0; | |||||
list-style: none; | |||||
background: #efefef; } | |||||
.tribute-container li { | |||||
padding: 5px 5px; | |||||
cursor: pointer; } | |||||
.tribute-container li.highlight, .tribute-container li:hover { | |||||
background: #ddd; } | |||||
.tribute-container li span { | |||||
font-weight: bold; } | |||||
.tribute-container li.no-match { | |||||
cursor: default; } | |||||
.tribute-container .menu-highlighted { | |||||
font-weight: bold; } |
<script src='{{ URLJoin .RecaptchaURL "api.js"}}' async></script> | <script src='{{ URLJoin .RecaptchaURL "api.js"}}' async></script> | ||||
{{end}} | {{end}} | ||||
{{end}} | {{end}} | ||||
{{if .RequireTribute}} | |||||
<script src="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.min.js"></script> | |||||
{{end}} | |||||
<script src="{{StaticUrlPrefix}}/fomantic/semantic.min.js?v={{MD5 AppVer}}"></script> | <script src="{{StaticUrlPrefix}}/fomantic/semantic.min.js?v={{MD5 AppVer}}"></script> | ||||
<script src="{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script> | <script src="{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script> | ||||
{{template "custom/footer" .}} | {{template "custom/footer" .}} |
{{if .RequireSimpleMDE}} | {{if .RequireSimpleMDE}} | ||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | ||||
{{end}} | {{end}} | ||||
{{if .RequireTribute}} | |||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css"> | |||||
{{end}} | |||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}"> | ||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}"> | ||||
<noscript> | <noscript> |
'{{StaticUrlPrefix}}/js/swagger.js?v={{MD5 AppVer}}', | '{{StaticUrlPrefix}}/js/swagger.js?v={{MD5 AppVer}}', | ||||
'{{StaticUrlPrefix}}/js/dropzone.js', | '{{StaticUrlPrefix}}/js/dropzone.js', | ||||
'{{StaticUrlPrefix}}/js/datetimepicker.js', | '{{StaticUrlPrefix}}/js/datetimepicker.js', | ||||
'{{StaticUrlPrefix}}/js/tribute.js', | |||||
'{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js', | '{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js', | ||||
'{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js', | '{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js', | ||||
'{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js', | '{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js', | ||||
'{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css', | '{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css', | ||||
'{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css', | '{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css', | ||||
'{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css', | '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css', | ||||
'{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css', | |||||
{{if .IsSigned }} | {{if .IsSigned }} | ||||
{{ if ne .SignedUser.Theme "gitea" }} | {{ if ne .SignedUser.Theme "gitea" }} | ||||
'{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}', | '{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}', |
import {emojiKeys, emojiHTML, emojiString} from './emoji.js'; | import {emojiKeys, emojiHTML, emojiString} from './emoji.js'; | ||||
import {uniq} from '../utils.js'; | |||||
export const issuesTribute = window.config.Tribute ? new Tribute({ | |||||
values: window.config.tributeValues, | |||||
noMatchTemplate() { return null }, | |||||
menuItemTemplate(item) { | |||||
const div = $('<div/>'); | |||||
div.append($('<img/>', {src: item.original.avatar})); | |||||
div.append($('<span/>', {class: 'name'}).text(item.original.name)); | |||||
if (item.original.fullname && item.original.fullname !== '') { | |||||
div.append($('<span/>', {class: 'fullname'}).text(item.original.fullname)); | |||||
} | |||||
return div.html(); | |||||
} | |||||
}) : null; | |||||
export const emojiTribute = window.config.Tribute ? new Tribute({ | |||||
collection: [{ | |||||
trigger: ':', | |||||
requireLeadingSpace: true, | |||||
values(query, cb) { | |||||
const matches = []; | |||||
for (const name of emojiKeys) { | |||||
if (name.includes(query)) { | |||||
matches.push(name); | |||||
if (matches.length > 5) break; | |||||
function makeCollections({mentions, emoji}) { | |||||
const collections = []; | |||||
if (mentions) { | |||||
collections.push({ | |||||
trigger: ':', | |||||
requireLeadingSpace: true, | |||||
values: (query, cb) => { | |||||
const matches = []; | |||||
for (const name of emojiKeys) { | |||||
if (name.includes(query)) { | |||||
matches.push(name); | |||||
if (matches.length > 5) break; | |||||
} | |||||
} | } | ||||
cb(matches); | |||||
}, | |||||
lookup: (item) => item, | |||||
selectTemplate: (item) => { | |||||
if (typeof item === 'undefined') return null; | |||||
return emojiString(item.original); | |||||
}, | |||||
menuItemTemplate: (item) => { | |||||
return `<div class="tribute-item">${emojiHTML(item.original)}<span>${item.original}</span></div>`; | |||||
} | } | ||||
cb(matches); | |||||
}, | |||||
lookup(item) { | |||||
return item; | |||||
}, | |||||
selectTemplate(item) { | |||||
if (typeof item === 'undefined') return null; | |||||
return emojiString(item.original); | |||||
}, | |||||
menuItemTemplate(item) { | |||||
return `<div class="tribute-item">${emojiHTML(item.original)}<span>${item.original}</span></div>`; | |||||
} | |||||
}] | |||||
}) : null; | |||||
export function initTribute() { | |||||
if (!window.config.Tribute) return; | |||||
let content = document.getElementById('content'); | |||||
if (content !== null) { | |||||
issuesTribute.attach(content); | |||||
}); | |||||
} | } | ||||
const emojiInputs = document.querySelectorAll('.emoji-input'); | |||||
if (emojiInputs.length > 0) { | |||||
emojiTribute.attach(emojiInputs); | |||||
if (emoji) { | |||||
collections.push({ | |||||
values: window.config.tributeValues, | |||||
noMatchTemplate: () => null, | |||||
menuItemTemplate: (item) => { | |||||
return ` | |||||
<div class="tribute-item"> | |||||
<img src="${item.original.avatar}"/> | |||||
<span class="name">${item.original.name}</span> | |||||
${item.original.fullname && item.original.fullname !== '' ? `<span class="fullname">${item.original.fullname}</span>` : ''} | |||||
</div> | |||||
`; | |||||
} | |||||
}); | |||||
} | } | ||||
content = document.getElementById('content'); | |||||
if (content !== null) { | |||||
emojiTribute.attach(document.getElementById('content')); | |||||
return collections; | |||||
} | |||||
export default async function attachTribute(elementOrNodeList, {mentions, emoji} = {}) { | |||||
if (!window.config.Tribute || !elementOrNodeList) return; | |||||
const nodes = Array.from('length' in elementOrNodeList ? elementOrNodeList : [elementOrNodeList]); | |||||
if (!nodes.length) return; | |||||
const mentionNodes = nodes.filter((node) => { | |||||
return mentions || node.id === 'content'; | |||||
}); | |||||
const emojiNodes = nodes.filter((node) => { | |||||
return emoji || node.id === 'content' || node.classList.contains('emoji-input'); | |||||
}); | |||||
const uniqueNodes = uniq([...mentionNodes, ...emojiNodes]); | |||||
if (!uniqueNodes.length) return; | |||||
const {default: Tribute} = await import(/* webpackChunkName: "tribute" */'tributejs'); | |||||
const collections = makeCollections({ | |||||
mentions: mentions || mentionNodes.length > 0, | |||||
emoji: emoji || emojiNodes.length > 0, | |||||
}); | |||||
const tribute = new Tribute({collection: collections}); | |||||
for (const node of uniqueNodes) { | |||||
tribute.attach(node); | |||||
} | } | ||||
return tribute; | |||||
} | } |
import initClipboard from './features/clipboard.js'; | import initClipboard from './features/clipboard.js'; | ||||
import initUserHeatmap from './features/userheatmap.js'; | import initUserHeatmap from './features/userheatmap.js'; | ||||
import initDateTimePicker from './features/datetimepicker.js'; | import initDateTimePicker from './features/datetimepicker.js'; | ||||
import {initTribute, issuesTribute, emojiTribute} from './features/tribute.js'; | |||||
import attachTribute from './features/tribute.js'; | |||||
import createDropzone from './features/dropzone.js'; | import createDropzone from './features/dropzone.js'; | ||||
import highlight from './features/highlight.js'; | import highlight from './features/highlight.js'; | ||||
import ActivityTopAuthors from './components/ActivityTopAuthors.vue'; | import ActivityTopAuthors from './components/ActivityTopAuthors.vue'; | ||||
if ($editContentZone.html().length === 0) { | if ($editContentZone.html().length === 0) { | ||||
$editContentZone.html($('#edit-content-form').html()); | $editContentZone.html($('#edit-content-form').html()); | ||||
$textarea = $editContentZone.find('textarea'); | $textarea = $editContentZone.find('textarea'); | ||||
issuesTribute.attach($textarea.get()); | |||||
emojiTribute.attach($textarea.get()); | |||||
attachTribute($textarea.get(), {mentions: true, emoji: true}); | |||||
let dz; | let dz; | ||||
const $dropzone = $editContentZone.find('.dropzone'); | const $dropzone = $editContentZone.find('.dropzone'); | ||||
$(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); | $(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); | ||||
simplemde.codemirror.setOption('extraKeys', { | simplemde.codemirror.setOption('extraKeys', { | ||||
Enter: () => { | Enter: () => { | ||||
if (!(issuesTribute.isActive || emojiTribute.isActive)) { | |||||
const tributeContainer = document.querySelector('.tribute-container'); | |||||
if (tributeContainer && tributeContainer.style.display !== 'none') { | |||||
return CodeMirror.Pass; | return CodeMirror.Pass; | ||||
} | } | ||||
}, | }, | ||||
cm.execCommand('delCharBefore'); | cm.execCommand('delCharBefore'); | ||||
} | } | ||||
}); | }); | ||||
issuesTribute.attach(simplemde.codemirror.getInputField()); | |||||
emojiTribute.attach(simplemde.codemirror.getInputField()); | |||||
attachTribute(simplemde.codemirror.getInputField(), {mentions: true, emoji: true}); | |||||
return simplemde; | return simplemde; | ||||
} | } | ||||
initContextPopups(); | initContextPopups(); | ||||
initNotificationsTable(); | initNotificationsTable(); | ||||
initNotificationCount(); | initNotificationCount(); | ||||
initTribute(); | |||||
// Repo clone url. | // Repo clone url. | ||||
if ($('#repo-clone-url').length > 0) { | if ($('#repo-clone-url').length > 0) { | ||||
// parallel init of lazy-loaded features | // parallel init of lazy-loaded features | ||||
await Promise.all([ | await Promise.all([ | ||||
highlight(document.querySelectorAll('pre code')), | highlight(document.querySelectorAll('pre code')), | ||||
attachTribute(document.querySelectorAll('#content, .emoji-input')), | |||||
initGitGraph(), | initGitGraph(), | ||||
initClipboard(), | initClipboard(), | ||||
initUserHeatmap(), | initUserHeatmap(), |
export function isDarkTheme() { | export function isDarkTheme() { | ||||
return document.documentElement.classList.contains('theme-arc-green'); | return document.documentElement.classList.contains('theme-arc-green'); | ||||
} | } | ||||
// removes duplicate elements in an array | |||||
export function uniq(arr) { | |||||
return Array.from(new Set(arr)); | |||||
} |
@import "~tributejs/dist/tribute.css"; | |||||
.tribute-container { | .tribute-container { | ||||
box-shadow: 0 1px 3px 1px #c7c7c7; | |||||
ul { | |||||
background: #ffffff; | |||||
} | |||||
li { | |||||
padding: 8px 12px; | |||||
border-bottom: 1px solid #dcdcdc; | |||||
img { | |||||
display: inline-block; | |||||
vertical-align: middle; | |||||
width: 28px; | |||||
height: 28px; | |||||
margin-right: 5px; | |||||
} | |||||
span.fullname { | |||||
font-weight: normal; | |||||
font-size: .8rem; | |||||
margin-left: 3px; | |||||
} | |||||
} | |||||
li.highlight, | |||||
li:hover { | |||||
background: #2185d0; | |||||
color: #ffffff; | |||||
} | |||||
box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .25); | |||||
border-radius: .25rem; | |||||
} | |||||
.tribute-container ul { | |||||
margin-top: 0 !important; | |||||
background: #ffffff !important; | |||||
} | |||||
.tribute-container li { | |||||
padding: 3px .5rem !important; | |||||
} | |||||
.tribute-container li span.fullname { | |||||
font-weight: normal; | |||||
font-size: .8rem; | |||||
margin-left: 3px; | |||||
} | |||||
.tribute-container li.highlight, | |||||
.tribute-container li:hover { | |||||
background: #2185d0 !important; | |||||
color: #ffffff !important; | |||||
} | } | ||||
.tribute-item { | .tribute-item { | ||||
align-items: center; | align-items: center; | ||||
} | } | ||||
.tribute-item .emoji { | |||||
.tribute-item .emoji, | |||||
.tribute-item img[src*="/avatar/"] { | |||||
margin-right: .5rem; | margin-right: .5rem; | ||||
} | } | ||||
.tribute-container img { | |||||
width: 1.5rem !important; | |||||
height: 1.5rem !important; | |||||
} |
.repository.release #release-list > li .detail .dot { | .repository.release #release-list > li .detail .dot { | ||||
background-color: #888; | background-color: #888; | ||||
} | } | ||||
.tribute-container { | |||||
box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .6); | |||||
} | |||||
.tribute-container ul { | |||||
background: #2d303b !important; | |||||
} | |||||
.tribute-container li.highlight, | |||||
.tribute-container li:hover { | |||||
background: #728e5e !important; | |||||
} |