121 lines
3.1 KiB
TypeScript
121 lines
3.1 KiB
TypeScript
/**
|
|
* Shared utilities for Storage browser components
|
|
*/
|
|
|
|
export interface IMoveValidation {
|
|
valid: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
/**
|
|
* Format a byte size into a human-readable string
|
|
*/
|
|
export function formatSize(bytes?: number): string {
|
|
if (bytes === undefined || bytes === null) return '-';
|
|
if (bytes === 0) return '0 B';
|
|
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
let size = bytes;
|
|
let unitIndex = 0;
|
|
|
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
size /= 1024;
|
|
unitIndex++;
|
|
}
|
|
|
|
return `${size.toFixed(unitIndex > 0 ? 1 : 0)} ${units[unitIndex]}`;
|
|
}
|
|
|
|
/**
|
|
* Format a count into a compact human-readable string
|
|
*/
|
|
export function formatCount(count?: number): string {
|
|
if (count === undefined || count === null) return '';
|
|
if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;
|
|
if (count >= 1000) return `${(count / 1000).toFixed(1)}K`;
|
|
return count.toString();
|
|
}
|
|
|
|
/**
|
|
* Extract the file name from a path
|
|
*/
|
|
export function getFileName(path: string): string {
|
|
const parts = path.replace(/\/$/, '').split('/');
|
|
return parts[parts.length - 1] || path;
|
|
}
|
|
|
|
/**
|
|
* Validates if a move operation is allowed
|
|
*/
|
|
export function validateMove(sourceKey: string, destPrefix: string): IMoveValidation {
|
|
if (sourceKey.endsWith('/')) {
|
|
if (destPrefix.startsWith(sourceKey)) {
|
|
return { valid: false, error: 'Cannot move a folder into itself' };
|
|
}
|
|
}
|
|
|
|
const sourceParent = getParentPrefix(sourceKey);
|
|
if (sourceParent === destPrefix) {
|
|
return { valid: false, error: 'Item is already in this location' };
|
|
}
|
|
|
|
return { valid: true };
|
|
}
|
|
|
|
/**
|
|
* Gets the parent prefix (directory) of a given key
|
|
*/
|
|
export function getParentPrefix(key: string): string {
|
|
const trimmed = key.endsWith('/') ? key.slice(0, -1) : key;
|
|
const lastSlash = trimmed.lastIndexOf('/');
|
|
return lastSlash >= 0 ? trimmed.substring(0, lastSlash + 1) : '';
|
|
}
|
|
|
|
/**
|
|
* Get content type from file extension
|
|
*/
|
|
export function getContentType(ext: string): string {
|
|
const contentTypes: Record<string, string> = {
|
|
json: 'application/json',
|
|
txt: 'text/plain',
|
|
html: 'text/html',
|
|
css: 'text/css',
|
|
js: 'application/javascript',
|
|
ts: 'text/typescript',
|
|
md: 'text/markdown',
|
|
xml: 'application/xml',
|
|
yaml: 'text/yaml',
|
|
yml: 'text/yaml',
|
|
csv: 'text/csv',
|
|
};
|
|
return contentTypes[ext] || 'application/octet-stream';
|
|
}
|
|
|
|
/**
|
|
* Get default content for a new file based on extension
|
|
*/
|
|
export function getDefaultContent(ext: string): string {
|
|
const defaults: Record<string, string> = {
|
|
json: '{\n \n}',
|
|
html: '<!DOCTYPE html>\n<html>\n<head>\n <title></title>\n</head>\n<body>\n \n</body>\n</html>',
|
|
md: '# Title\n\n',
|
|
txt: '',
|
|
};
|
|
return defaults[ext] || '';
|
|
}
|
|
|
|
/**
|
|
* Parse a prefix into cumulative path segments
|
|
*/
|
|
export function getPathSegments(prefix: string): string[] {
|
|
if (!prefix) return [];
|
|
const parts = prefix.split('/').filter(p => p);
|
|
const segments: string[] = [];
|
|
let cumulative = '';
|
|
for (const part of parts) {
|
|
cumulative += part + '/';
|
|
segments.push(cumulative);
|
|
}
|
|
return segments;
|
|
}
|