Signed-off-by: Lucas Azevedo <lhs_azevedo@hotmail.com>tags/v28.0.0beta1
z-index: 10; | z-index: 10; | ||||
padding: 0 20px 0 0; | padding: 0 20px 0 0; | ||||
} | } | ||||
/* System tags */ | |||||
.system-tags { | |||||
--min-size: 32px; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
min-width: calc(var(--min-size) * 2); | |||||
max-width: 300px; | |||||
.system-tags__tag { | |||||
padding: 5px 10px; | |||||
border: 1px solid; | |||||
border-radius: var(--border-radius-pill); | |||||
border-color: var(--color-border); | |||||
color: var(--color-text-maxcontrast); | |||||
height: var(--min-size); | |||||
white-space: nowrap; | |||||
overflow: hidden; | |||||
text-overflow: ellipsis; | |||||
line-height: 22px; // min-size - 2 * 5px padding | |||||
text-align: center; | |||||
&--more { | |||||
overflow: visible; | |||||
text-overflow: initial; | |||||
} | |||||
// Proper spacing if multiple shown | |||||
& + .system-tags__tag { | |||||
margin-left: 5px; | |||||
} | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
"sidebarpreviewmanager.js", | "sidebarpreviewmanager.js", | ||||
"sidebarpreviewtext.js", | "sidebarpreviewtext.js", | ||||
"tagsplugin.js", | "tagsplugin.js", | ||||
"systemtagsplugin.js", | |||||
"templates.js" | "templates.js" | ||||
] | ] |
/* | |||||
* Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> | |||||
* | |||||
* This file is licensed under the Affero General Public License version 3 | |||||
* or later. | |||||
* | |||||
* See the COPYING-README file. | |||||
* | |||||
*/ | |||||
/* global Handlebars */ | |||||
(function (OCA) { | |||||
_.extend(OC.Files.Client, { | |||||
PROPERTY_SYSTEM_TAGS: '{' + OC.Files.Client.NS_NEXTCLOUD + '}system-tags', | |||||
}); | |||||
OCA.Files = OCA.Files || {}; | |||||
/** | |||||
* Extends the file actions and file list to add system tags inline | |||||
* | |||||
* @namespace OCA.Files.SystemTagsPlugin | |||||
*/ | |||||
OCA.Files.SystemTagsPlugin = { | |||||
name: 'SystemTags', | |||||
allowedLists: [ | |||||
'files', | |||||
'favorites', | |||||
'shares.self', | |||||
'shares.others', | |||||
'shares.link' | |||||
], | |||||
_buildTagSpan: function(tag, isMore = false) { | |||||
var $tag = $('<li class="system-tags__tag"></li>'); | |||||
$tag.text(tag).addClass(isMore ? 'system-tags__tag--more' : ''); | |||||
return $tag; | |||||
}, | |||||
_buildTagsUI: function(tags) { | |||||
$systemTags = $('<ul class="system-tags"></ul>'); | |||||
if (tags.length === 1) { | |||||
$systemTags.attr('aria-label', t('files', 'This file has the tag {tag}', { tag: tags[0] })); | |||||
} else if (tags.length > 1) { | |||||
var firstTags = tags.slice(0, -1).join(', '); | |||||
var lastTag = tags[tags.length - 1]; | |||||
$systemTags.attr('aria-label', t('files', 'This file has the tags {firstTags} and {lastTag}', { firstTags, lastTag })); | |||||
} | |||||
if (tags.length > 0) { | |||||
$systemTags.append(this._buildTagSpan(tags[0])); | |||||
} | |||||
// More tags than the one we're showing | |||||
if (tags.length > 1) { | |||||
$moreTag = this._buildTagSpan('+' + (tags.length - 1), true) | |||||
$moreTag.attr('title', tags.slice(1).join(', ')); | |||||
$systemTags.append($moreTag); | |||||
} | |||||
return $systemTags; | |||||
}, | |||||
_extendFileList: function(fileList) { | |||||
var self = this; | |||||
// extend row prototype | |||||
var oldCreateRow = fileList._createRow; | |||||
fileList._createRow = function(fileData) { | |||||
var $tr = oldCreateRow.apply(this, arguments); | |||||
var systemTags = fileData.systemTags || []; | |||||
// Update tr data list | |||||
$tr.attr('data-systemTags', systemTags.join('|')); | |||||
// No tags, no need to do anything | |||||
if (systemTags.length === 0) { | |||||
return $tr; | |||||
} | |||||
// Build tags ui and inject | |||||
$systemTags = self._buildTagsUI.apply(self, [systemTags]) | |||||
$systemTags.insertAfter($tr.find('td.filename .nametext')); | |||||
return $tr; | |||||
}; | |||||
var oldElementToFile = fileList.elementToFile; | |||||
fileList.elementToFile = function ($el) { | |||||
var fileInfo = oldElementToFile.apply(this, arguments); | |||||
var systemTags = $el.attr('data-systemTags'); | |||||
fileInfo.systemTags = systemTags?.split?.('|') || []; | |||||
return fileInfo; | |||||
}; | |||||
var oldGetWebdavProperties = fileList._getWebdavProperties; | |||||
fileList._getWebdavProperties = function () { | |||||
var props = oldGetWebdavProperties.apply(this, arguments); | |||||
props.push(OC.Files.Client.PROPERTY_SYSTEM_TAGS); | |||||
return props; | |||||
}; | |||||
fileList.filesClient.addFileInfoParser(function (response) { | |||||
var data = {}; | |||||
var props = response.propStat[0].properties; | |||||
var systemTags = props[OC.Files.Client.PROPERTY_SYSTEM_TAGS] || []; | |||||
if (systemTags && systemTags.length) { | |||||
data.systemTags = systemTags | |||||
.filter(xmlvalue => xmlvalue.namespaceURI === OC.Files.Client.NS_NEXTCLOUD && xmlvalue.nodeName.split(':')[1] === 'system-tag') | |||||
.map(xmlvalue => xmlvalue.textContent || xmlvalue.text); | |||||
} | |||||
return data; | |||||
}); | |||||
}, | |||||
attach: function(fileList) { | |||||
if (this.allowedLists.indexOf(fileList.id) < 0) { | |||||
return; | |||||
} | |||||
this._extendFileList(fileList); | |||||
}, | |||||
}; | |||||
}) | |||||
(OCA); | |||||
OC.Plugins.register('OCA.Files.FileList', OCA.Files.SystemTagsPlugin); |
/** | |||||
* @copyright Copyright (c) 2023 Lucas Azevedo <lhs_azevedo@hotmail.com> | |||||
* | |||||
* @author Lucas Azevedo <lhs_azevedo@hotmail.com> | |||||
* | |||||
* @license AGPL-3.0-or-later | |||||
* | |||||
* This program is free software: you can redistribute it and/or modify | |||||
* it under the terms of the GNU Affero General Public License as | |||||
* published by the Free Software Foundation, either version 3 of the | |||||
* License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
* GNU Affero General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Affero General Public License | |||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
* | |||||
*/ | |||||
import { FileAction, Node, registerDavProperty, registerFileAction } from "@nextcloud/files"; | |||||
import { translate as t } from '@nextcloud/l10n' | |||||
import '../css/fileEntryInlineSystemTags.scss' | |||||
const getNodeSystemTags = function (node: Node): string[] { | |||||
const tags = node.attributes?.['system-tags']?.['system-tag'] as string|string[]|undefined | |||||
if (tags === undefined) { | |||||
return [] | |||||
} | |||||
return [tags].flat() | |||||
} | |||||
const renderTag = function (tag: string, isMore: boolean = false): HTMLElement { | |||||
const tagElement = document.createElement('li') | |||||
tagElement.classList.add('files-list__system-tag') | |||||
tagElement.innerText = tag | |||||
if (isMore) { | |||||
tagElement.classList.add('files-list__system-tag--more') | |||||
} | |||||
return tagElement; | |||||
} | |||||
export const action = new FileAction({ | |||||
id: 'system-tags', | |||||
displayName: () => '', | |||||
iconSvgInline: () => '', | |||||
exec: async () => null, | |||||
async renderInline(node: Node) { | |||||
// Ensure we have the system tags as an array | |||||
const tags = getNodeSystemTags(node) | |||||
if (tags.length === 0) { | |||||
return null | |||||
} | |||||
const systemTagsElement = document.createElement('ul') | |||||
systemTagsElement.classList.add('files-list__system-tags') | |||||
if (tags.length === 1) { | |||||
systemTagsElement.setAttribute('aria-label', t('files', 'This file has the tag {tag}', { tag: tags[0] })); | |||||
} else { | |||||
var firstTags = tags.slice(0, -1).join(', '); | |||||
var lastTag = tags[tags.length - 1]; | |||||
systemTagsElement.setAttribute('aria-label', t('files', 'This file has the tags {firstTags} and {lastTag}', { firstTags, lastTag })); | |||||
} | |||||
systemTagsElement.append(renderTag(tags[0])) | |||||
// More tags than the one we're showing | |||||
if (tags.length > 1) { | |||||
const moreTagElement = renderTag('+' + (tags.length - 1), true) | |||||
moreTagElement.setAttribute('title', tags.slice(1).join(', ')); | |||||
systemTagsElement.append(moreTagElement); | |||||
} | |||||
return systemTagsElement | |||||
} | |||||
}) | |||||
registerDavProperty('nc:system-tags') | |||||
registerFileAction(action) |
.files-list__system-tags { | |||||
--min-size: 32px; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
min-width: calc(var(--min-size) * 2); | |||||
max-width: 300px; | |||||
} | |||||
.files-list__system-tag { | |||||
padding: 5px 10px; | |||||
border: 1px solid; | |||||
border-radius: var(--border-radius-pill); | |||||
border-color: var(--color-border); | |||||
color: var(--color-text-maxcontrast); | |||||
height: var(--min-size); | |||||
white-space: nowrap; | |||||
overflow: hidden; | |||||
text-overflow: ellipsis; | |||||
line-height: 22px; // min-size - 2 * 5px padding | |||||
text-align: center; | |||||
&--more { | |||||
overflow: visible; | |||||
text-overflow: initial; | |||||
} | |||||
// Proper spacing if multiple shown | |||||
& + .files-list__system-tag { | |||||
margin-left: 5px; | |||||
} | |||||
} |
import './actions/downloadAction' | import './actions/downloadAction' | ||||
import './actions/editLocallyAction' | import './actions/editLocallyAction' | ||||
import './actions/favoriteAction' | import './actions/favoriteAction' | ||||
import './actions/inlineSystemTagsAction' | |||||
import './actions/openFolderAction' | import './actions/openFolderAction' | ||||
import './actions/openInFilesAction.js' | import './actions/openInFilesAction.js' | ||||
import './actions/renameAction' | import './actions/renameAction' |