Contenuti del tema della scheda utente sticky

TL;DR Penso che questo sia ciò che stai cercando

JS del tema

api.modifyClass("component:user-card-contents", {
  didInsertElement() {
    this._super(...arguments);
    $("html").off(this.clickOutsideEventName);
  },
  actions: {
    closeCard() {
      this._close();
    }
  }
});

e poi aggiungi questo al tuo template da qualche parte

{{d-button
  class="btn-flat"
  action=(action "closeCard")
  icon="times"
}}

La versione lunga

Probabilmente conosci già la maggior parte di questo dato che hai già lavorato sul tuo tema, ma cercherò di essere un po’ più dettagliato per un pubblico più ampio.

La prima cosa che farei è cercare, sia localmente che su Github. Su Github otterresti qualcosa come questo. Nella maggior parte dei casi, un termine di ricerca avrà più di un risultato e dovrai essere più specifico o esaminare manualmente i risultati per trovare qualcosa di simile a ciò che desideri.

Quindi, ora siamo arrivati a questo file

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Questo file è un Mixin. Perché lo menziono? Perché devi essere consapevole che i mixin possono essere condivisi in molti luoghi diversi. In questo caso, è utilizzato sia per le schede utente che per le schede gruppo. Quindi, le modifiche che apporti qui influenzeranno entrambe.

Se cerchi nel file, troverai che clickOutsideEventName viene definito per la prima volta qui

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

poi passato qui come proprietà

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

e infine consumato qui

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Bene, ma cosa significa tutto ciò? Beh, se guardi dove viene aggiunto tutto questo codice, noterai che si trova all’interno di didInsertElement

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Le guide Ember affermano

Ember garantisce che, al momento in cui viene chiamato didInsertElement():

  1. L’elemento del componente è stato creato e inserito nel DOM.
  2. L’elemento del componente è accessibile tramite la proprietà this.element del componente.

Perché ne abbiamo bisogno? Perché abbiamo bisogno di un gestore mousedown diverso per le schede utente e quelle gruppo. Se torniamo indietro un po’, noterai che l’id dell’elemento viene utilizzato in clickOutsideEventName

Che, come abbiamo discusso sopra, viene poi passato come proprietà.

Ora, passiamo a come tutto questo si relaziona a ciò che stai facendo.

Stai cercando di impedire che le schede vengano chiuse quando l’utente clicca fuori da esse. Quindi, proviamo a trovare un modo per farlo. Se ricordi, abbiamo discusso di come clickOutsideEventName alla fine venga consumato qui

In sintesi, questo aggiunge un gestore mousedown all’elemento HTML quando una scheda viene inserita (alla prima visualizzazione della pagina). Quindi controlliamo l’obiettivo dell’evento mousedown. Se l’obiettivo si trova all’interno della scheda, usciamo. Se è fuori dalla scheda, la chiudiamo chiamando this._close()

_close() è definito qui

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

E questo è ciò che dovrai chiamare quando aggiungi il tuo pulsante di chiusura, ma torneremo su questo più tardi.

Ora, l’obiettivo è rimuovere questo gestore mousedown se vuoi che i clic fuori dalla scheda non la chiudano. Quindi, come facciamo? Beh, dovremo modificare didInsertElement() ed ecco come può essere fatto.

I temi Discourse hanno accesso alla plugin API, che contiene molti metodi utilizzabili. Ci sono ulteriori dettagli sui più comunemente usati qui

Se ricordi, il file su cui stiamo lavorando è un Mixin. Un Mixin è una classe Ember. Quindi, il metodo che useremo è modifyClass

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/lib/plugin-api.js#L121-L124

Quando usi modifyClass, puoi aggiungere, modificare o sovrascrivere completamente un metodo di classe. Ci concentreremo sulla modifica di un metodo, poiché è ciò che vuoi fare.

Vogliamo modificare didInsertElement(), quindi possiamo fare qualcosa come questo

api.modifyClass('mixin:card-contents-base', {
  didInsertElement() {
    console.log("foo");
  }
});

e provarlo…

beh, non ha funzionato.

Perché? Beh, risulta che il metodo modifyClass al momento non supporta la modifica dei Mixin (per quanto ho testato). Farò una nota per capire perché è così e verificare se possiamo risolvere il problema. Per ora, torniamo a ciò che vuoi fare.

Beh, non possiamo modificare i Mixin, quindi suppongo che siamo bloccati, vero? No. Scaviamo un po’ più a fondo.

Come ho menzionato prima, quel Mixin è utilizzato sia dalla scheda utente user-card-contents che dalla group-card-contents, componenti Ember components (ancora una volta, perché i Mixin sono progettati per rendere il codice riutilizzabile)

Quindi, esaminiamo il componente user-card-contents qui

discourse/app/assets/javascripts/discourse/app/components/user-card-contents.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Se leggi attentamente, noterai che prima importiamo il Mixin di cui abbiamo parlato qui

e poi creiamo un nuovo componente Ember e gli passiamo il Mixin.

Cosa significa? Significa che didInsertElement() per user-card-contents è effettivamente ereditato dal Mixin. Lo stesso vale per group-card-contents.

Dove ci lascia questo? Beh, ci sono buone e cattive notizie. La buona notizia è che se vuoi apportare modifiche a user-card-contents senza influenzare group-card-contents, allora puoi farlo! La cattiva notizia è che se vuoi che le tue modifiche si applichino a entrambi, dovrai duplicare del codice.

Torniamo a modifyClass e riproviamo con user-card-contents. Quindi qualcosa come questo:

api.modifyClass('component:user-card-contents', {
  didInsertElement() {
    console.log("foo");
  }
});

e vediamo cosa succede…

voalà :tada:

La modifica è registrata e possiamo vederla nella console, ma non siamo ancora arrivati alla fine.

Quindi, ora che possiamo modificare didInsertElement(), torniamo a ciò che stai cercando di fare. Se ricordi, il gestore mousedown è definito in didInsertElement qui

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Quindi, cosa possiamo farci? Beh, è semplice come questo

$("html").off(clickOutsideEventName)

Funzionerà? No. Se fai questo

api.modifyClass('component:user-card-contents', {
  didInsertElement() {
    $("html").off(clickOutsideEventName)
  }
});

finirai con qualcosa di rotto. Perché? Perché non è una modifica del metodo. È una sovrascrittura completa del metodo core didInsertElement() per quel componente. Quindi, se fai così, nessuno del codice core viene effettivamente applicato.

Come lo risolviamo? Beh, risulta che Ember ha una cosa chiamata this._super(...arguments)

Cosa fa? Ti permette di aggiungere o anteporre codice in aggiunta a ciò che il metodo della classe ha già. Ad esempio, se fai questo

api.modifyClass('component:user-card-contents', {
  didInsertElement() {
    // codice che vuoi aggiungere
    $("html").off(clickOutsideEventName);
    // codice dal core
    this._super(...arguments);
  }
});

allora il codice che vuoi aggiungere verrà eseguito prima di tutto il resto qui

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Tuttavia, se fai questo…

api.modifyClass('component:user-card-contents', {
  didInsertElement() {
    // codice dal core
    this._super(...arguments);
    // codice che vuoi aggiungere
    $("html").off(clickOutsideEventName);
  }
});

allora il tuo codice verrà eseguito dopo che tutto qui è stato eseguito

discourse/app/assets/javascripts/discourse/app/mixins/card-contents-base.js at e5dc843185feb268c277bb0ee4db9666d6452783 · discourse/discourse · GitHub

Questo è un ottimo modo per mantenere il tuo tema resistente alle modifiche nel core, dato che tutto nel core viene eseguito per primo, poi il tuo codice viene eseguito dopo. Quindi, proviamo di nuovo e vediamo cosa succede.

api.modifyClass('component:user-card-contents', {
  didInsertElement() {
    // codice dal core
    this._super(...arguments);
    // codice che vuoi aggiungere
    $("html").off(clickOutsideEventName);
  }
});

e…

Perché sta succedendo questo? È a causa di un contesto di codice diverso. Nel file del componente Ember, clickOutsideEventName è già definito al momento in cui viene consumato. Il tuo tema è in un file diverso, quindi clickOutsideEventName non è definito lì.

Come lo risolviamo? Ricordi questo?

clickOutsideEventName è una proprietà del componente, quindi se usi this.clickOutsideEventName, dovrebbe funzionare. Proviamo.

api.modifyClass('component:user-card-contents', {
  didInsertElement() {
    // codice dal core
    this._super(...arguments);
    // codice che vuoi aggiungere
    $("html").off(this.clickOutsideEventName);
  },
});

E infatti funziona :tada:

Le schede utente ora si aprono senza errori e cliccare ovunque fuori non fa nulla.

Puoi fare esattamente la stessa cosa per le schede gruppo così

api.modifyClass('component:group-card-contents', {
  didInsertElement() {
    // codice dal core
    this._super(...arguments);
    // codice che vuoi aggiungere
    $("html").off(this.clickOutsideEventName);
  },
});

L’unica cosa che manca è collegare quel pulsante di chiusura al metodo _close() di cui abbiamo parlato prima e ci sono tre passaggi per questo.

  1. aggiungi un’azione closeCard (puoi chiamarla come vuoi)
  2. aggiungi un pulsante al template della scheda utente
  3. chiama quell’azione quando il pulsante viene cliccato.

Mi atterrò alle schede utente per semplicità. Quindi, aggiungiamo questo (insieme a quanto già discusso)

api.modifyClass("component:user-card-contents", {
  didInsertElement() {
    this._super(...arguments);
    $("html").off(this.clickOutsideEventName);
  },
  // nuove cose
  actions: {
    closeCard() {
      this._close();
    }
  }
});

tutto ciò che fa è chiamare il metodo core _close() ogni volta che viene attivata l’azione personalizzata closeCard.

Successivamente, dobbiamo aggiungere un pulsante al template della scheda utente o qualcosa di simile

{{d-button
  class="btn-flat"
  action=(action "closeCard")
  icon="times"
}}

I miei risultati approssimativi sono come questo

E naturalmente puoi fare qualcosa di simile per le schede gruppo come ho menzionato sopra.

Lascio l’implementazione per le schede gruppo e mobile come esercizio per te, dato che useresti essenzialmente gli stessi concetti di cui abbiamo parlato sopra se volessi apportare modifiche a quelle, ma fammi sapere se incontri problemi.