@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 +2 -0
- package/package.json +1 -1
- package/src/index.js +42 -2
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.
|
|
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=${
|
|
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)}">×</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())
|