Browse Source

Render inline system tags using new DAV properties API

Signed-off-by: Lucas Azevedo <lhs_azevedo@hotmail.com>
tags/v28.0.0beta1
Lucas Azevedo 8 months ago
parent
commit
146e9eeb9f

+ 0
- 34
apps/files/css/files.scss View File

@@ -406,40 +406,6 @@ table {
z-index: 10;
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;
}
}
}
}
}


+ 0
- 1
apps/files/js/merged-index.json View File

@@ -24,6 +24,5 @@
"sidebarpreviewmanager.js",
"sidebarpreviewtext.js",
"tagsplugin.js",
"systemtagsplugin.js",
"templates.js"
]

+ 0
- 128
apps/files/js/systemtagsplugin.js View File

@@ -1,128 +0,0 @@
/*
* 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);

+ 88
- 0
apps/files/src/actions/inlineSystemTagsAction.ts View File

@@ -0,0 +1,88 @@
/**
* @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)

+ 32
- 0
apps/files/src/css/fileEntryInlineSystemTags.scss View File

@@ -0,0 +1,32 @@
.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;
}
}

+ 1
- 0
apps/files/src/main.ts View File

@@ -5,6 +5,7 @@ import './actions/deleteAction'
import './actions/downloadAction'
import './actions/editLocallyAction'
import './actions/favoriteAction'
import './actions/inlineSystemTagsAction'
import './actions/openFolderAction'
import './actions/openInFilesAction.js'
import './actions/renameAction'

Loading…
Cancel
Save