npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cbm-common/xml-modal

v0.0.1

Published

Componente modal de Angular para visualizar y gestionar contenido XML con resaltado de sintaxis, funcionalidades de copiado y descarga. Implementa un modal moderno con soporte para temas oscuro/claro y animaciones personalizadas.

Readme

XML Modal

Componente modal de Angular para visualizar y gestionar contenido XML con resaltado de sintaxis, funcionalidades de copiado y descarga. Implementa un modal moderno con soporte para temas oscuro/claro y animaciones personalizadas.

📦 Instalación

npm install @cbm-common/xml-modal

⚙️ Configuración

Importación del Componente

El componente es standalone, por lo que se puede importar directamente:

import { CbmXmlModalComponent } from '@cbm-common/xml-modal';

@Component({
  selector: 'app-xml-viewer',
  standalone: true,
  imports: [CbmXmlModalComponent],
  template: `
    <cbm-xml-modal
      [isOpen]="showModal"
      [xml]="xmlContent"
      [titleText]="customTitle"
      [fileName]="downloadFileName"
      [theme]="selectedTheme"
      (close)="showModal = false"
      (copy)="onXmlCopied($event)"
      (download)="onXmlDownloaded($event)"
    />
  `
})
export class XmlViewerComponent {
  showModal = false;
  xmlContent = '<root><item>Example XML</item></root>';
  customTitle = 'Vista XML Personalizada';
  downloadFileName = 'document.xml';
  selectedTheme: 'light' | 'dark' = 'dark';

  onXmlCopied(xml: string) {
    console.log('XML copiado:', xml);
  }

  onXmlDownloaded(blob: Blob) {
    console.log('XML descargado:', blob);
  }
}

🎯 Propiedades de Entrada (Inputs)

isOpen: boolean (Requerido)

Controla la visibilidad del modal.

// Abrir modal
this.showModal = true;

// Cerrar modal
this.closeModal = false;

xml: string

Contenido XML a mostrar en el modal.

xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<root>
  <item id="1">
    <name>Ejemplo</name>
    <value>123</value>
  </item>
</root>`;

titleText: string

Texto personalizado para el título del modal.

titleText = 'Documento XML';

fileName: string

Nombre del archivo para la descarga (por defecto usa timestamp).

fileName = 'factura-electronica.xml';

theme: 'light' | 'dark'

Tema de resaltado de sintaxis para el código XML.

// Tema oscuro (predeterminado)
theme = 'dark';

// Tema claro
theme = 'light';

📤 Eventos de Salida (Outputs)

open: void

Se emite cuando el modal se abre.

(open)="onModalOpen()"

close: void

Se emite cuando el modal se cierra.

(close)="onModalClose()"

copy: string

Se emite cuando se copia el contenido XML al portapapeles.

(copy)="onXmlCopied($event)"

Parámetro del evento:

  • xml: string - Contenido XML copiado

download: Blob

Se emite cuando se descarga el archivo XML.

(download)="onXmlDownloaded($event)"

Parámetro del evento:

  • blob: Blob - Archivo Blob del XML descargado

🎨 Características Visuales

Temas de Resaltado de Sintaxis

Tema Oscuro (Predeterminado)

  • Fondo: GitHub Dark (#0d1117)
  • Texto: GitHub Light (#c9d1d9)
  • Palabras clave: Rojo coral (#ff7b72)
  • Cadenas: Azul claro (#a5c9ea)
  • Comentarios: Gris (#8b949e)
  • Etiquetas: Verde (#7ee787)
  • Atributos: Púrpura (#d2a8ff)

Tema Claro

  • Fondo: Blanco (#ffffff)
  • Texto: GitHub Dark (#24292f)
  • Palabras clave: Rojo (#cf222e)
  • Cadenas: Azul oscuro (#0a3069)
  • Comentarios: Gris (#6e7781)
  • Etiquetas: Verde oscuro (#116329)
  • Atributos: Púrpura (#8250df)

Diseño Responsive

  • Modal adaptable: Dimensiones dinámicas (85vw x 70dvh)
  • Scroll automático: Para contenido largo
  • Mobile-friendly: Optimizado para dispositivos táctiles

Animaciones

  • Entrada/Salida: Transiciones suaves con opacidad
  • Modal: Animación de deslizamiento desde arriba
  • Botones: Efectos hover y transiciones de color

🔧 Funcionalidades

Copiar al Portapapeles

copyXml(): void {
  // Copia el contenido XML completo al portapapeles
  // Muestra notificación de éxito/error
}

Descargar Archivo

downloadXml(): void {
  // Crea un Blob con el contenido XML
  // Descarga el archivo con el nombre especificado
  // Tipo MIME: text/xml
}

Navegación por Teclado

  • ESC: Cerrar modal
  • Tab: Navegación entre elementos interactivos

🚀 Ejemplos de Uso

Ejemplo Básico

@Component({
  selector: 'app-simple-xml-viewer',
  standalone: true,
  imports: [CbmXmlModalComponent],
  template: `
    <div class="xml-viewer">
      <button (click)="openModal()" class="btn-primary">
        Ver XML
      </button>

      <cbm-xml-modal
        [isOpen]="showModal"
        [xml]="xmlData"
        (close)="showModal = false"
      />
    </div>
  `
})
export class SimpleXmlViewerComponent {
  showModal = false;
  xmlData = '<root><message>Hello World</message></root>';

  openModal() {
    this.showModal = true;
  }
}

Ejemplo con Configuración Completa

@Component({
  selector: 'app-advanced-xml-viewer',
  standalone: true,
  imports: [CbmXmlModalComponent],
  template: `
    <div class="advanced-viewer">
      <div class="controls">
        <select [(ngModel)]="selectedTheme">
          <option value="light">Tema Claro</option>
          <option value="dark">Tema Oscuro</option>
        </select>

        <input
          [(ngModel)]="customTitle"
          placeholder="Título del modal"
        >

        <input
          [(ngModel)]="fileName"
          placeholder="Nombre del archivo"
        >
      </div>

      <button (click)="loadXml()" class="btn-primary">
        Cargar XML desde API
      </button>

      <cbm-xml-modal
        [isOpen]="showModal"
        [xml]="xmlContent"
        [titleText]="customTitle"
        [fileName]="fileName"
        [theme]="selectedTheme"
        (close)="showModal = false"
        (copy)="handleCopy($event)"
        (download)="handleDownload($event)"
      />
    </div>
  `
})
export class AdvancedXmlViewerComponent {
  showModal = false;
  selectedTheme: 'light' | 'dark' = 'dark';
  customTitle = 'Documento XML';
  fileName = 'documento.xml';
  xmlContent = '';

  loadXml() {
    // Simular carga desde API
    this.xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
<invoice>
  <header>
    <number>001-001-0000001</number>
    <date>2024-01-15</date>
    <customer>
      <name>Cliente Ejemplo</name>
      <id>123456789</id>
    </customer>
  </header>
  <details>
    <item>
      <description>Producto 1</description>
      <quantity>2</quantity>
      <price>100.00</price>
    </item>
  </details>
</invoice>`;

    this.showModal = true;
  }

  handleCopy(xml: string) {
    console.log('XML copiado exitosamente:', xml.length, 'caracteres');
  }

  handleDownload(blob: Blob) {
    console.log('Archivo descargado:', blob.size, 'bytes');
  }
}

Ejemplo con Integración de Servicios

@Component({
  selector: 'app-xml-from-service',
  standalone: true,
  imports: [CbmXmlModalComponent],
  template: `
    <div class="service-integration">
      <div class="actions">
        <button (click)="loadInvoiceXml()" class="btn-primary">
          Ver Factura XML
        </button>

        <button (click)="loadReceiptXml()" class="btn-secondary">
          Ver Recibo XML
        </button>
      </div>

      <cbm-xml-modal
        [isOpen]="showModal"
        [xml]="currentXml"
        [titleText]="modalTitle"
        [fileName]="downloadName"
        [theme]="selectedTheme"
        (close)="showModal = false"
        (copy)="onXmlAction('copied', $event)"
        (download)="onXmlAction('downloaded', $event)"
      />
    </div>
  `
})
export class XmlFromServiceComponent {
  showModal = false;
  currentXml = '';
  modalTitle = '';
  downloadName = '';
  selectedTheme: 'light' | 'dark' = 'dark';

  constructor(private xmlService: XmlDocumentService) {}

  loadInvoiceXml() {
    this.xmlService.getInvoiceXml('INV001').subscribe({
      next: (xml) => {
        this.currentXml = xml;
        this.modalTitle = 'Factura Electrónica XML';
        this.downloadName = 'factura-INV001.xml';
        this.showModal = true;
      },
      error: (error) => {
        console.error('Error cargando XML:', error);
      }
    });
  }

  loadReceiptXml() {
    this.xmlService.getReceiptXml('REC001').subscribe({
      next: (xml) => {
        this.currentXml = xml;
        this.modalTitle = 'Recibo XML';
        this.downloadName = 'recibo-REC001.xml';
        this.showModal = true;
      }
    });
  }

  onXmlAction(action: string, data: string | Blob) {
    if (action === 'copied') {
      this.xmlService.logAction('XML_COPIED', data as string);
    } else {
      this.xmlService.logAction('XML_DOWNLOADED', (data as Blob).size.toString());
    }
  }
}

Ejemplo con Tema Dinámico

@Component({
  selector: 'app-dynamic-theme-xml',
  standalone: true,
  imports: [CbmXmlModalComponent],
  template: `
    <div class="theme-switcher" [class.dark]="isDarkMode">
      <div class="theme-toggle">
        <button (click)="toggleTheme()" class="theme-btn">
          {{ isDarkMode ? '☀️' : '🌙' }}
          {{ isDarkMode ? 'Modo Claro' : 'Modo Oscuro' }}
        </button>
      </div>

      <cbm-xml-modal
        [isOpen]="showModal"
        [xml]="xmlContent"
        [theme]="isDarkMode ? 'dark' : 'light'"
        (close)="showModal = false"
      />
    </div>
  `,
  styles: [`
    .theme-switcher.dark {
      background-color: #1a1a1a;
      color: #ffffff;
    }

    .theme-btn {
      padding: 0.5rem 1rem;
      border: 1px solid #ccc;
      border-radius: 0.5rem;
      background: white;
      cursor: pointer;
    }

    .dark .theme-btn {
      background: #333;
      border-color: #555;
      color: #fff;
    }
  `]
})
export class DynamicThemeXmlComponent {
  isDarkMode = false;
  showModal = false;
  xmlContent = '<root><theme>Example</theme></root>';

  toggleTheme() {
    this.isDarkMode = !this.isDarkMode;
  }

  openModal() {
    this.showModal = true;
  }
}

🎭 Configuración de Temas Avanzada

Importación de Temas Personalizados

// En el componente que usa xml-modal
import 'highlight.js/styles/github-dark.css'; // Tema oscuro moderno
import 'highlight.js/styles/github.css'; // Tema claro

// Opciones alternativas de temas oscuros:
// import 'highlight.js/styles/vs2015.css'; // VS Code oscuro
// import 'highlight.js/styles/atom-one-dark.css'; // Atom oscuro
// import 'highlight.js/styles/monokai.css'; // Monokai clásico
// import 'highlight.js/styles/dracula.css'; // Dracula

// Opciones alternativas de temas claros:
// import 'highlight.js/styles/vs.css'; // VS claro
// import 'highlight.js/styles/atom-one-light.css'; // Atom claro
// import 'highlight.js/styles/xcode.css'; // Xcode

Personalización de Estilos CSS

/* Personalizar colores del tema oscuro */
.hljs-dark-theme {
  --hljs-background: #0d1117;
  --hljs-foreground: #c9d1d9;
  --hljs-keyword: #ff7b72;
  --hljs-string: #a5c9ea;
  --hljs-comment: #8b949e;
  --hljs-tag: #7ee787;
  --hljs-attr: #d2a8ff;
}

/* Personalizar colores del tema claro */
.hljs-light-theme {
  --hljs-background: #ffffff;
  --hljs-foreground: #24292f;
  --hljs-keyword: #cf222e;
  --hljs-string: #0a3069;
  --hljs-comment: #6e7781;
  --hljs-tag: #116329;
  --hljs-attr: #8250df;
}

⚠️ Manejo de Errores

Errores de Copiado

// El componente maneja automáticamente errores de:
- Permisos denegados para portapapeles
- Contenido vacío
- Errores de navegador

Errores de Descarga

// El componente maneja automáticamente errores de:
- Creación de Blob fallida
- Errores de URL.createObjectURL
- Problemas de descarga del navegador

Notificaciones

El componente integra automáticamente con CbmNotificationService para mostrar:

  • Éxito: "Copiado exitosamente" / "Descargando archivo..."
  • Error: Mensajes específicos de error

📋 Dependencias

Peer Dependencies (Requeridas)

{
  "@angular/common": ">=20.1.5",
  "@angular/core": ">=20.1.5",
  "ngx-highlightjs": "14.0.1",
  "@cbm-common/tooltip-directive": "0.0.x",
  "@cbm-common/notification-service": "0.0.x"
}

Dependencias de Desarrollo

{
  "tailwindcss": "^4.1.11",
  "postcss": "^8.5.6",
  "@tailwindcss/postcss": "^4.1.11"
}

🛠️ Desarrollo

Estructura del Proyecto

xml-modal/
├── src/
│   ├── lib/
│   │   ├── xml-modal.component.ts       # Componente principal
│   │   ├── xml-modal.component.html     # Template del modal
│   │   ├── xml-modal.component.css      # Estilos específicos
│   │   ├── styles.css                   # Estilos Tailwind + temas
│   │   ├── animations.ts                # Animaciones Angular
│   │   └── index.ts                     # Exportaciones
│   └── public-api.ts                    # API pública
├── ng-package.json                      # Configuración empaquetado
├── package.json                         # Dependencias
└── README.md                            # Esta documentación

Construcción

# Construir la librería
ng build xml-modal

# Construir en modo watch
ng build xml-modal --watch

# Construir para producción
ng build xml-modal --configuration production

Pruebas

# Ejecutar pruebas unitarias
ng test xml-modal

# Ejecutar pruebas con coverage
ng test xml-modal --code-coverage

Desarrollo con Tailwind

# Compilar estilos Tailwind
npm run tailwind

# Modo watch para desarrollo
npm run tailwind --watch

🎯 Mejores Prácticas

1. Configuración de Tema Global

// En app.config.ts o main.ts
import 'highlight.js/styles/github-dark.css'; // Tema global

// O configurar dinámicamente
export const HIGHLIGHT_THEMES = {
  dark: () => import('highlight.js/styles/github-dark.css'),
  light: () => import('highlight.js/styles/github.css')
};

2. Manejo de Contenido Grande

// Para XML muy grandes, considerar lazy loading
@Component({...})
export class LargeXmlViewerComponent {
  xmlChunks: string[] = [];
  currentChunk = 0;

  loadXmlChunk() {
    // Cargar chunks del XML progresivamente
    this.xmlService.getXmlChunk(this.currentChunk).subscribe(chunk => {
      this.xmlChunks.push(chunk);
      this.currentXml = this.xmlChunks.join('');
    });
  }
}

3. Integración con Sistema de Archivos

// Para guardar/cargar archivos XML
export class XmlFileManager {
  async saveXmlToFile(xml: string, filename: string) {
    const blob = new Blob([xml], { type: 'text/xml' });
    const url = URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();

    URL.revokeObjectURL(url);
  }

  async loadXmlFromFile(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = () => reject(reader.error);
      reader.readAsText(file);
    });
  }
}

🤝 Contribución

  1. Fork el repositorio
  2. Crea una rama para tu feature (git checkout -b feature/nueva-funcionalidad)
  3. Commit tus cambios (git commit -am 'Agrega nueva funcionalidad')
  4. Push a la rama (git push origin feature/nueva-funcionalidad)
  5. Abre un Pull Request

📄 Licencia

Este proyecto está bajo la Licencia MIT - ver el archivo LICENSE para más detalles.

📞 Soporte

Para soporte técnico o reportes de bugs, contacta al equipo de desarrollo de CBM:

🔄 Changelog

v0.0.1

  • ✅ Componente modal standalone con animaciones personalizadas
  • ✅ Resaltado de sintaxis XML con ngx-highlightjs
  • ✅ Soporte para temas oscuro y claro
  • ✅ Funcionalidades de copiar y descargar XML
  • ✅ Integración con servicios de notificación y tooltips
  • ✅ Diseño responsive con Tailwind CSS
  • ✅ Encapsulación Shadow DOM emulada
  • ✅ Documentación completa en español

Nota: Este componente está optimizado para trabajar con archivos XML de facturación electrónica y documentos similares. Para contenido XML muy grande, considera implementar paginación o carga diferida.