import { Meta, Source } from '@storybook/addon-docs'
import '../../styles/shared.css';

<Meta title="Guide du dev/Classes utilitaires/Utilitaires DOM & Navigateur" />


<div className="header">
  <h1>Utilitaires DOM & Navigateur</h1>
  <p>Ces utilitaires facilitent les interactions avec le DOM et le navigateur, comme la copie dans le presse-papier, le téléchargement de fichiers et l'optimisation des performances.</p>
</div>

# Utilitaires DOM & Navigateur

Ces utilitaires facilitent les interactions avec le DOM et le navigateur, comme la copie dans le presse-papier, le téléchargement de fichiers et l'optimisation des performances.

## copyToClipboard

Cette fonction permet de copier un texte dans le presse-papier de l'utilisateur.

### Importation

<Source dark code={`import { copyToClipboard } from '@cnamts/synapse'
`}/>

### Utilisation

<Source dark code={`// Copier un texte simple
copyToClipboard('Texte à copier')
`}/>

### Exemple pratique

<Source dark code={`<template>
  <div>
    <v-text-field
      v-model="referenceCode"
      label="Code de référence"
      readonly
    />
    <v-btn
      icon
      @click="copyCode"
    >
      <v-icon>mdi-content-copy</v-icon>
    </v-btn>
    <v-snackbar
      v-model="showSnackbar"
      :timeout="3000"
    >
      Code copié dans le presse-papier
    </v-snackbar>
  </div>
</template>

<script>
import { ref } from 'vue'
import { copyToClipboard } from '@cnamts/synapse'

export default {
  setup() {
    const referenceCode = ref('REF-12345-ABCDE')
    const showSnackbar = ref(false)
    
    const copyCode = () => {
      copyToClipboard(referenceCode.value)
      showSnackbar.value = true
    }
    
    return {
      referenceCode,
      showSnackbar,
      copyCode
    }
  }
}
</script>
`}/>

## downloadFile

Cette fonction permet de télécharger un fichier à partir d'un contenu (Blob, ArrayBuffer ou string).

### Importation

<Source dark code={`import { downloadFile } from '@cnamts/synapse'
`}/>

### Signature

<Source dark code={`downloadFile(
  content: ArrayBuffer | ArrayBufferView | Blob | string,
  filename: string,
  type: string,        // Type MIME du fichier
  utf8Bom?: boolean    // Ajouter le BOM UTF-8 (utile pour Excel)
): void
`}/>

### Utilisation

<Source dark code={`// Télécharger un fichier texte
downloadFile('Contenu du fichier', 'mon-fichier.txt', 'text/plain')

// Télécharger un fichier CSV avec BOM UTF-8 (pour Excel)
downloadFile('Nom,Prénom\nDupont,Jean', 'export.csv', 'text/csv', true)

// Télécharger un fichier à partir d'un Blob
const blob = new Blob(['contenu du fichier'], { type: 'text/plain' })
downloadFile(blob, 'mon-fichier.txt', 'text/plain')
`}/>

### Exemple pratique

<Source dark code={`<template>
  <div>
    <v-btn
      color="primary"
      @click="downloadReport"
    >
      Télécharger le rapport
    </v-btn>
  </div>
</template>

<script>
import { downloadFile } from '@cnamts/synapse'

export default {
  props: {
    reportContent: {
      type: String,
      required: true
    },
    reportName: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const downloadReport = () => {
      downloadFile(
        props.reportContent,
        props.reportName,
        'application/pdf'
      )
    }
    
    return {
      downloadReport
    }
  }
}
</script>
`}/>

### Génération de fichiers à la volée

Vous pouvez également générer des fichiers à la volée à partir de données :

<Source dark code={`<template>
  <v-btn @click="exportToCsv">Exporter en CSV</v-btn>
</template>

<script>
import { downloadFile } from '@cnamts/synapse'

export default {
  props: {
    data: {
      type: Array,
      required: true
    }
  },
  setup(props) {
    const exportToCsv = () => {
      // Créer l'en-tête du CSV
      const headers = Object.keys(props.data[0]).join(',')
      
      // Créer les lignes de données
      const rows = props.data.map(item => 
        Object.values(item).join(',')
      )
      
      // Combiner en-tête et lignes
      const csvContent = [headers, ...rows].join('\n')
      
      // Télécharger le fichier CSV avec BOM UTF-8 pour Excel
      downloadFile(csvContent, 'export.csv', 'text/csv;charset=utf-8;', true)
    }
    
    return {
      exportToCsv
    }
  }
}
</script>
`}/>

## throttleDisplayFn

Cette fonction limite le nombre d'appels à une fonction d'affichage pour améliorer les performances, particulièrement utile pour les événements qui se déclenchent fréquemment comme le scroll ou le resize.

### Importation

<Source dark code={`import { throttleDisplayFn } from '@cnamts/synapse'
`}/>

### Signature

<Source dark code={`throttleDisplayFn<F extends (...args: unknown[]) => void>(
  fn: F,        // La fonction à throttler
  delay: number // Délai minimum entre deux appels (en ms)
): (...args: Parameters<F>) => void
`}/>

### Utilisation

<Source dark code={`// Fonction originale qui pourrait être appelée trop fréquemment
const updateUI = () => {
  // Mise à jour de l'interface utilisateur
  console.log('Mise à jour de l\'UI')
}

// Création d'une version limitée de la fonction (max 1 appel toutes les 100ms)
const throttledUpdateUI = throttleDisplayFn(updateUI, 100)

// Utilisation dans un événement qui se déclenche fréquemment
window.addEventListener('scroll', throttledUpdateUI)
`}/>

### Exemple pratique

<Source dark code={`<template>
  <div>
    <div class="scroll-container" ref="scrollContainer">
      <div v-for="item in items" :key="item.id" class="item">
        {{ item.name }}
      </div>
    </div>
    <div v-if="showScrollIndicator" class="scroll-indicator">
      Faites défiler pour voir plus
    </div>
  </div>
</template>

<script>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { throttleDisplayFn } from '@cnamts/synapse'

export default {
  props: {
    items: {
      type: Array,
      required: true
    }
  },
  setup() {
    const scrollContainer = ref(null)
    const showScrollIndicator = ref(true)
    
    const checkScroll = () => {
      if (!scrollContainer.value) return
      
      const { scrollTop, scrollHeight, clientHeight } = scrollContainer.value
      
      // Masquer l'indicateur si l'utilisateur a défilé jusqu'à un certain point
      if (scrollTop > 100 || scrollTop + clientHeight >= scrollHeight - 20) {
        showScrollIndicator.value = false
      } else {
        showScrollIndicator.value = true
      }
    }
    
    // Utilisation de throttleDisplayFn pour limiter les appels (max 1 appel toutes les 100ms)
    const throttledCheckScroll = throttleDisplayFn(checkScroll, 100)
    
    onMounted(() => {
      if (scrollContainer.value) {
        scrollContainer.value.addEventListener('scroll', throttledCheckScroll)
      }
    })
    
    onBeforeUnmount(() => {
      if (scrollContainer.value) {
        scrollContainer.value.removeEventListener('scroll', throttledCheckScroll)
      }
    })
    
    return {
      scrollContainer,
      showScrollIndicator
    }
  }
}
</script>

<style scoped>
.scroll-container {
  height: 300px;
  overflow-y: auto;
}

.item {
  padding: 16px;
  border-bottom: 1px solid #eee;
}

.scroll-indicator {
  position: fixed;
  bottom: 20px;
  right: 20px;
  padding: 8px 16px;
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  border-radius: 4px;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0% { opacity: 0.7; }
  50% { opacity: 1; }
  100% { opacity: 0.7; }
}
</style>
`}/>

## Bonnes pratiques

### Pour copyToClipboard

- Fournir un retour visuel à l'utilisateur lorsque la copie est réussie
- Vérifier que le texte à copier n'est pas vide ou null
- La fonction est synchrone et utilise l'API Clipboard moderne avec fallback sur execCommand

### Pour downloadFile

- Utiliser des noms de fichiers descriptifs et incluant l'extension appropriée
- Toujours spécifier le type MIME approprié (3ème paramètre)
- Utiliser `utf8Bom: true` pour les fichiers CSV destinés à Excel (gestion des accents)
- La fonction est synchrone et crée un lien de téléchargement temporaire

### Pour throttleDisplayFn

- Utiliser pour les événements à haute fréquence (scroll, resize, mousemove)
- Ne pas l'utiliser pour des événements critiques nécessitant une réponse immédiate
- Nettoyer les écouteurs d'événements dans onBeforeUnmount pour éviter les fuites de mémoire
