style(ui): request that all types defined
- check in more restrictive mode = request types - define main types in static/js/core/types.js
This commit is contained in:
@@ -3,11 +3,21 @@
|
||||
* Centralized global helpers for date/size/text formatting and XSS-safe escaping.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string') return '';
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} bytes
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
@@ -19,11 +29,21 @@ function formatFileSize(bytes) {
|
||||
}
|
||||
|
||||
/// Formats a byte count for quota display. When bytes is 0, returns "∞" (unlimited).
|
||||
/**
|
||||
*
|
||||
* @param {number} bytes
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatQuotaSize(bytes) {
|
||||
if (bytes === 0) return '∞';
|
||||
return formatFileSize(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Date | number| null} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatDateTime(value) {
|
||||
if (!value) return '';
|
||||
let dateValue;
|
||||
@@ -38,6 +58,11 @@ function formatDateTime(value) {
|
||||
return `${dateValue.toLocaleDateString()} ${dateValue.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Date | number| null} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatDateShort(value) {
|
||||
if (!value) return 'N/A';
|
||||
const dateValue = typeof value === 'number' ? new Date(value * 1000) : new Date(value);
|
||||
@@ -49,20 +74,27 @@ function formatDateShort(value) {
|
||||
});
|
||||
}
|
||||
|
||||
const TEXT_TYPES = [
|
||||
'application/json',
|
||||
'application/xml',
|
||||
'application/javascript',
|
||||
'application/x-sh',
|
||||
'application/x-yaml',
|
||||
'application/toml',
|
||||
'application/x-toml',
|
||||
'application/sql'
|
||||
];
|
||||
// FIXME: move is to another file
|
||||
/**
|
||||
*
|
||||
* @param {string} mimeType
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isTextViewable(mimeType) {
|
||||
if (!mimeType) return false;
|
||||
if (mimeType.startsWith('text/')) return true;
|
||||
const textTypes = [
|
||||
'application/json',
|
||||
'application/xml',
|
||||
'application/javascript',
|
||||
'application/x-sh',
|
||||
'application/x-yaml',
|
||||
'application/toml',
|
||||
'application/x-toml',
|
||||
'application/sql'
|
||||
];
|
||||
return textTypes.includes(mimeType);
|
||||
|
||||
return TEXT_TYPES.includes(mimeType);
|
||||
}
|
||||
|
||||
export { escapeHtml, formatDateShort, formatDateTime, formatFileSize, formatQuotaSize, isTextViewable };
|
||||
|
||||
@@ -18,6 +18,7 @@ if (!supportedLocales.includes(currentLocale)) {
|
||||
}
|
||||
|
||||
// Cache for translations
|
||||
/** @type {Record<string, Object>} */
|
||||
const translations = {};
|
||||
|
||||
/**
|
||||
|
||||
@@ -450,7 +450,7 @@ function replaceIconsInElement(container) {
|
||||
if (!container) container = document.body;
|
||||
const icons = container.querySelectorAll('i[class*="fa-"]');
|
||||
for (let i = 0; i < icons.length; i++) {
|
||||
const el = icons[i];
|
||||
const el = /** @type {HTMLElement} */ (icons[i]);
|
||||
const classes = el.className.split(/\s+/);
|
||||
|
||||
// Find the icon name (fa-xxx)
|
||||
|
||||
@@ -96,7 +96,7 @@ function createLanguageSelector(containerId = 'language-selector') {
|
||||
option.className = `language-option${lang.code === currentLocale ? ' active' : ''}`;
|
||||
option.setAttribute('role', 'option');
|
||||
option.setAttribute('data-lang', lang.code);
|
||||
option.setAttribute('aria-selected', lang.code === currentLocale);
|
||||
option.setAttribute('aria-selected', String(lang.code === currentLocale));
|
||||
option.innerHTML = `
|
||||
<span class="lang-flag">${lang.flag}</span>
|
||||
<span class="lang-name">${lang.name}</span>
|
||||
@@ -134,7 +134,7 @@ function createLanguageSelector(containerId = 'language-selector') {
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!container.contains(e.target)) {
|
||||
if (!container.contains(/** @type {Node | null } */ (e.target))) {
|
||||
closeDropdown(container);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,9 +53,9 @@ const Modal = {
|
||||
this.closeBtn = document.getElementById('modal-close-btn');
|
||||
|
||||
// Event listeners
|
||||
this.cancelBtn.addEventListener('click', () => this.close(false));
|
||||
this.closeBtn.addEventListener('click', () => this.close(false));
|
||||
this.confirmBtn.addEventListener('click', () => this.confirm());
|
||||
this.cancelBtn?.addEventListener('click', () => this.close(false));
|
||||
this.closeBtn?.addEventListener('click', () => this.close(false));
|
||||
this.confirmBtn?.addEventListener('click', () => this.confirm());
|
||||
|
||||
// Close on overlay click
|
||||
this.overlay.addEventListener('click', (e) => {
|
||||
@@ -65,7 +65,7 @@ const Modal = {
|
||||
});
|
||||
|
||||
// Handle Enter and Escape keys
|
||||
this.input.addEventListener('keydown', (e) => {
|
||||
this.input?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this.confirm();
|
||||
|
||||
@@ -34,7 +34,7 @@ const notifications = (() => {
|
||||
|
||||
bellBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const open = wrapper.classList.toggle('open');
|
||||
const open = wrapper?.classList.toggle('open');
|
||||
bellBtn.classList.toggle('active', open);
|
||||
|
||||
// Close user-menu if it's open
|
||||
@@ -46,7 +46,7 @@ const notifications = (() => {
|
||||
|
||||
// Close on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!wrapper.contains(e.target)) {
|
||||
if (!wrapper?.contains(/** @type {Node | null} */ (e.target))) {
|
||||
close();
|
||||
}
|
||||
});
|
||||
@@ -63,8 +63,8 @@ const notifications = (() => {
|
||||
function close() {
|
||||
const bellBtn = $('notif-bell-btn');
|
||||
const wrapper = $('notif-wrapper');
|
||||
wrapper.classList.remove('open');
|
||||
bellBtn.classList.remove('active');
|
||||
wrapper?.classList.remove('open');
|
||||
bellBtn?.classList.remove('active');
|
||||
}
|
||||
|
||||
/* ── badge helpers ──────────────────────────────────────── */
|
||||
@@ -82,7 +82,7 @@ const notifications = (() => {
|
||||
if (!badge) return;
|
||||
if (_badgeCount > 0) {
|
||||
badge.classList.remove('hidden');
|
||||
badge.textContent = _badgeCount > 99 ? '99+' : _badgeCount;
|
||||
badge.textContent = _badgeCount > 99 ? '99+' : String(_badgeCount);
|
||||
} else {
|
||||
badge.classList.add('hidden');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @typedef {Object} FolderInfo
|
||||
* @property {string} category
|
||||
* @property {number} created_at - timestamp
|
||||
* @property {string} icon_class
|
||||
* @property {string} icon_special_class
|
||||
* @property {string} id the uniq id of the folder
|
||||
* @property {boolean} is_root
|
||||
* @property {number} modified_at
|
||||
* @property {string} name
|
||||
* @property {string} owner_id
|
||||
* @property {string|null} parent_id the folder parent (null if is_root)
|
||||
* @property {string} path the full path
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileInfo
|
||||
* @property {string} category
|
||||
* @property {number} created_at - timestamp
|
||||
* @property {string} icon_class
|
||||
* @property {string} icon_special_class
|
||||
* @property {string} id the uniq id of the folder
|
||||
* @property {string} mime_type
|
||||
* @property {number} modified_at - timestamp
|
||||
* @property {string} name
|
||||
* @property {string} owner_id
|
||||
* @property {string} folder_id the folder parent
|
||||
* @property {string} path the full path
|
||||
* @property {number} size
|
||||
* @property {string} size_formatted
|
||||
* @property {number} sort_date
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SharePermissions
|
||||
* @property {boolean} read
|
||||
* @property {boolean} reshare
|
||||
* @property {boolean} write
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Share
|
||||
* @property {number} access_count
|
||||
* @property {number} created_at - timestamp
|
||||
* @property {String} created_by
|
||||
* @property {number} expires_at - timestamp
|
||||
* @property {boolean} has_password
|
||||
* @property {string} id
|
||||
* @property {string} item_id
|
||||
* @property {string} item_name
|
||||
* @property {string} item_type
|
||||
* @property {SharePermissions} permissions
|
||||
* @property {string | null} token
|
||||
* @property {string} url
|
||||
*/
|
||||
Reference in New Issue
Block a user