@dotrino/support 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @dotrino/support
2
2
 
3
+ > **Parte del ecosistema [Dotrino](https://dotrino.com).** Misión: aplicaciones que resuelven problemas comunes, respetando tu privacidad — sin anuncios, sin cookies, sin rastreo de datos, sin vender tu identidad a nadie.
4
+
3
5
  Web Component (`<dotrino-support>`) con un **modal de soporte/donaciones**
4
6
  reutilizable por **cualquier app del ecosistema Dotrino**: las hechas en
5
7
  Vue 3 y las vanilla (HTML/JS), porque es un *custom element* estándar.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/support",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Web Component (custom element) de modal de soporte/donaciones reutilizable por cualquier app del ecosistema Dotrino. Autohosteado, sin JS de terceros ni cookies.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -28,6 +28,8 @@
28
28
  * message Cuerpo del modal (override)
29
29
  * lang 'es' | 'en' | 'auto' (default 'auto')
30
30
  * variant 'solid' (default) | 'ghost' — estilo del disparador
31
+ * x-handle Cuenta de X que se menciona al compartir en X ("vía @handle").
32
+ * Default '@imdotrino'. No cross-network: solo aplica a X.
31
33
  * Métodos: el.open(), el.close()
32
34
  * Eventos: 'cc-support-open', 'cc-support-close' (bubbles, composed)
33
35
  */
@@ -42,6 +44,7 @@ const I18N = {
42
44
  'Si esta herramienta te resulta útil, puedes dar una pequeña contribución. Las apps del ecosistema Dotrino son gratuitas y autohosteadas. ¡Gracias!',
43
45
  close: 'Cerrar',
44
46
  defaultLink: 'Donar',
47
+ contact: 'Contacto',
45
48
  hint: 'Donar/Compartir',
46
49
  shareHeading: 'Compartir',
47
50
  shareText: '¡Mira esto!',
@@ -62,6 +65,7 @@ const I18N = {
62
65
  'If you find this tool useful, you can make a small contribution. The Dotrino ecosystem apps are free and self-hosted. Thank you!',
63
66
  close: 'Close',
64
67
  defaultLink: 'Donate',
68
+ contact: 'Contact',
65
69
  hint: 'Donate/Share',
66
70
  shareHeading: 'Share',
67
71
  shareText: 'Check this out!',
@@ -338,6 +342,19 @@ const STYLE = `
338
342
  .link-ico { display: inline-flex; width: 1.4rem; height: 1.4rem; }
339
343
  .link-ico img { width: 100%; height: 100%; display: block; object-fit: contain; }
340
344
 
345
+ /* Botón de Contacto (opt-in), arriba de las donaciones. Contorno para
346
+ diferenciarlo del botón lleno de "Donar". */
347
+ .contact-cta {
348
+ display: inline-flex; align-items: center; justify-content: center;
349
+ min-width: 220px; margin: 0 auto 0.9rem; padding: 0.7rem 1.4rem;
350
+ font-family: inherit; font-weight: 600; font-size: 0.95rem;
351
+ border-radius: 50px; cursor: pointer;
352
+ background: transparent; color: var(--ds-accent, #3498db);
353
+ border: 1.5px solid var(--ds-accent, #3498db);
354
+ transition: all 0.25s ease;
355
+ }
356
+ .contact-cta:hover { background: var(--ds-accent, #3498db); color: #fff; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(52, 152, 219, 0.3); }
357
+
341
358
  /* Sección de compartir */
342
359
  .share {
343
360
  margin-top: 1.5rem;
@@ -475,7 +492,7 @@ function recordAppOpen(appId) {
475
492
 
476
493
  class DotrinoSupport extends HTMLElement {
477
494
  static get observedAttributes() {
478
- return ['href', 'links', 'cta', 'no-trigger', 'heading', 'message', 'lang', 'variant', 'inline', 'hint', 'coin', 'no-bubble', 'bubble-timeout', 'share-url', 'share-text', 'no-share', 'repo', 'bug-href', 'discord', 'app', 'no-count']
495
+ return ['href', 'links', 'cta', 'no-trigger', 'heading', 'message', 'lang', 'variant', 'inline', 'hint', 'coin', 'no-bubble', 'bubble-timeout', 'share-url', 'share-text', 'no-share', 'repo', 'bug-href', 'discord', 'app', 'no-count', 'contact', 'contact-label']
479
496
  }
480
497
 
481
498
  constructor() {
@@ -598,9 +615,13 @@ class DotrinoSupport extends HTMLElement {
598
615
  const url = this._shareUrl
599
616
  const u = encodeURIComponent(url)
600
617
  const text = encodeURIComponent(this._shareText)
618
+ // Al compartir en X mencionamos NUESTRA cuenta de esa misma red ("vía @handle"),
619
+ // no cross-network. Override por proyecto con el atributo `x-handle`; default Dotrino.
620
+ const xHandle = (this.getAttribute('x-handle') || '@imdotrino').trim().replace(/^@*/, '@')
621
+ const xText = encodeURIComponent(`${this._shareText} vía ${xHandle}`)
601
622
  return [
602
623
  { key: 'whatsapp', href: `https://wa.me/?text=${text}%20${u}` },
603
- { key: 'x', href: `https://twitter.com/intent/tweet?url=${u}&text=${text}` },
624
+ { key: 'x', href: `https://twitter.com/intent/tweet?url=${u}&text=${xText}` },
604
625
  { key: 'facebook', href: `https://www.facebook.com/sharer/sharer.php?u=${u}` },
605
626
  { key: 'instagram', href: null }, // copia al portapapeles
606
627
  ]
@@ -725,6 +746,15 @@ class DotrinoSupport extends HTMLElement {
725
746
  )
726
747
  .join('')
727
748
 
749
+ // Botón de Contacto (opt-in con el atributo `contact`), ARRIBA de las
750
+ // donaciones. No abre un enlace: dispara `cc-support-contact` (la app decide
751
+ // qué hacer, p. ej. abrir su formulario) y cierra el modal.
752
+ const wantsContact = this.hasAttribute('contact')
753
+ const contactLabel = this.getAttribute('contact-label') || t.contact
754
+ const contactHtml = wantsContact
755
+ ? `<button type="button" class="contact-cta" part="contact">${escapeHtml(contactLabel)}</button>`
756
+ : ''
757
+
728
758
  const wantsShare = !this.hasAttribute('no-share')
729
759
  const shareHtml = wantsShare
730
760
  ? `<div class="share" part="share">
@@ -765,6 +795,7 @@ class DotrinoSupport extends HTMLElement {
765
795
  <button type="button" class="close" aria-label="${escapeAttr(t.close)}">&times;</button>
766
796
  <h2 class="heading">${escapeHtml(heading)}</h2>
767
797
  <p class="message">${escapeHtml(message)}</p>
798
+ ${contactHtml}
768
799
  <div class="links">${linksHtml}</div>
769
800
  ${shareHtml}
770
801
  ${discordHtml}
@@ -786,6 +817,15 @@ class DotrinoSupport extends HTMLElement {
786
817
  )
787
818
  this.shadowRoot.querySelector('.close').addEventListener('click', () => this.close())
788
819
 
820
+ // Botón de Contacto: cierra el modal y avisa a la app (que abre su formulario).
821
+ const contactBtn = this.shadowRoot.querySelector('.contact-cta')
822
+ if (contactBtn) {
823
+ contactBtn.addEventListener('click', () => {
824
+ this.close()
825
+ this.dispatchEvent(new CustomEvent('cc-support-contact', { bubbles: true, composed: true }))
826
+ })
827
+ }
828
+
789
829
  // Compartir: el botón sin href (Instagram) copia el enlace al portapapeles.
790
830
  this.shadowRoot.querySelectorAll('.share-btn[data-share]').forEach((btn) => {
791
831
  btn.addEventListener('click', () => this._copyShareUrl())