Skip to content

KFileBrowser

KFileBrowser (aussi appelé KServerBrowser) est un module JavaScript que j’ai développé durant mon stage. Son objectif est de proposer un explorateur de fichiers modulaire et personnalisable, capable de générer automatiquement une arborescence à partir d’une API ou d’un fichier JSON.

Le module peut être intégré facilement dans n’importe quel site web et offre de nombreuses options de configuration :

  • choix du container d’intégration (ID ou élément HTML)
  • support de différentes sources de données (API distante ou JSON local)
  • personnalisation complète : thèmes, icônes par type de fichier, langues et modules de traduction
  • gestion des types de fichiers acceptés et possibilité de masquer automatiquement les dossiers vides
  • callbacks pour interagir avec l’application (validation d’un fichier, clic sur un élément, etc.)

Techniquement, le projet a été développé en TypeScript et construit avec Vite, ce qui m’a permis de bénéficier d’un environnement rapide et moderne pour compiler, tester et distribuer le module. J’ai notamment approfondi mes compétences en POO, en architecture de composants réutilisables, et en gestion dynamique de données.

Ce projet m’a donné l’occasion de concevoir une librairie robuste et réutilisable, pensée pour être intégrée dans différents contextes (sites vitrines, applications web ou outils internes), tout en étant suffisamment flexible pour s’adapter à divers besoins métiers. Code de configuration de KServer

js
import type {FileExplorerConfig} from "./interface/FileExplorerConfig.ts";
import type {FileItem} from "./interface/FileItem.ts";
import type {FileExplorerIcons} from "./interface/FileExplorerIcons.ts";
import type {FileTypesOptions} from "./interface/FileTypeOptions.ts";
import type {LangModule} from "./interface/LangModule.ts";
import fr from "./lang/fr.ts";
import {availableLangs} from "./lang";

export class ServerBrowser {
    private config: FileExplorerConfig;
    private container: HTMLElement;
    private selectedFile: FileItem | null = null;
    private icons: Required<FileExplorerIcons>;
    private fileType: Required<FileTypesOptions>;
    private navigationHistory: FileItem[] = [];
    private initialData: FileItem[] = [];
    private lang: LangModule;
    private validateButton?: HTMLButtonElement;
    private previewButton?: HTMLButtonElement;
    private acceptedExtensions: string[] = [];
    private autoHideEmptyFolders: boolean;

    constructor(config: FileExplorerConfig) {
        this.config = config;

        this.container =
            typeof config.containerId === 'string'
                ? document.getElementById(config.containerId)!
                : config.containerId;

        if (!this.container) {
            throw new Error('[KServerBrowser] - Container not found.');
        }

        this.icons = {
            folder: '📁',
            folderOpen: '📂',
            file: '📄',
            image: '🖼️',
            video: '🎥',
            pdf: '📑',
            ...config.icons,
        };

        this.fileType = {
            images: true,
            videos: true,
            ...config.fileTypes,
        };

        this.lang = {
            ...availableLangs[config.lang ?? 'fr'],
            ...config.langModule,
        };

        this.acceptedExtensions = config.acceptedTypeFiles?.map(ext => ext.toLowerCase()) ?? [];
        this.autoHideEmptyFolders = config.autoHideEmptyFolders ?? false;
        this.applyTheme(config.theme);

    }

Méthode permettant de crée l'arbre de fichier

js
    private renderExplorer(data: FileItem[]): void {
        this.container.innerHTML = '';

        const kcontainer = document.createElement('div');
        kcontainer.classList.add('kcontainer');

        const ul = document.createElement('ul');
        ul.classList.add('kfile-tree');
        this.buildTree(data, ul);

        const parentButton = document.createElement('div');
        parentButton.classList.add('kfooter');

        this.validateButton = document.createElement('button');
        this.validateButton.textContent = this.lang.validate;
        this.validateButton.disabled = true;
        this.validateButton.className = 'k-validate-btn';
        this.validateButton.onclick = () => this.validateSelectedFile();

        this.previewButton = document.createElement('button');
        this.previewButton.textContent = this.lang.preview;
        this.previewButton.className = 'k-preview-btn';
        this.previewButton.style.display = 'none';
        this.previewButton.onclick = () => this.previewSelectedImage();

        if (this.navigationHistory.length > 0) {
            const backBtn = document.createElement('button');
            backBtn.textContent = this.lang.back;
            backBtn.className = 'k-back-btn';
            backBtn.onclick = () => this.goBack();
            kcontainer.appendChild(backBtn);
        }

        kcontainer.appendChild(ul);
        parentButton.appendChild(this.previewButton);
        parentButton.appendChild(this.validateButton);
        kcontainer.appendChild(parentButton);
        this.container.appendChild(kcontainer);
        ul.addEventListener('click', this.handleClick.bind(this));
    }