Creare un campo personalizzato

Prima di creare un nuovo tipo di campo, valuta se uno degli altri metodi per personalizzare i campi soddisfa le tue esigenze. Se la tua applicazione deve memorizzare un nuovo tipo di valore o vuoi creare una nuova UI per un tipo di valore esistente, probabilmente devi creare un nuovo tipo di campo.

Per creare un nuovo campo:

  1. Implementa un costruttore.
  2. Registra una chiave JSON e implementa fromJson.
  3. Gestisci l'inizializzazione dell'interfaccia utente e dei listener di eventi on-block.
  4. Gestisci l'eliminazione dei listener di eventi (l'eliminazione della UI viene gestita per te).
  5. Implementa la gestione dei valori.
  6. Aggiungi una rappresentazione testuale del valore del campo per l'accessibilità.
  7. Aggiungi funzionalità aggiuntive, ad esempio:
  8. Configura altri aspetti del campo, ad esempio:

Questa sezione presuppone che tu abbia letto e conosca i contenuti di Anatomia di un campo.

Per un esempio di campo personalizzato, consulta la demo dei campi personalizzati.

Implementazione di un costruttore

Il costruttore del campo è responsabile dell'impostazione del valore iniziale del campo e, facoltativamente, dell'impostazione di un validator locale. Il costruttore del campo personalizzato viene chiamato durante l'inizializzazione del blocco di origine, indipendentemente dal fatto che il blocco di origine sia definito in JSON o JavaScript. Pertanto, il campo personalizzato non ha accesso al blocco di origine durante la costruzione.

Il seguente codice campione crea un campo personalizzato denominato GenericField:

class GenericField extends Blockly.Field {
  constructor(value, validator) {
    super(value, validator);

    this.SERIALIZABLE = true;
  }
}

Firma del metodo

I costruttori di campi in genere accettano un valore e un validatore locale. Il valore è facoltativo e, se non lo trasmetti (o se trasmetti un valore che non supera la convalida della classe), verrà utilizzato il valore predefinito della superclasse. Per la classe predefinita Field, questo valore è null. Se non vuoi questo valore predefinito, assicurati di passare un valore adatto. Il parametro validator è presente solo per i campi modificabili e in genere è contrassegnato come facoltativo. Scopri di più sui validatori nella documentazione sui validatori.

Struttura

La logica all'interno del costruttore deve seguire questo flusso:

  1. Chiama il supercostruttore ereditato (tutti i campi personalizzati devono ereditare da Blockly.Field o da una delle sue sottoclassi) per inizializzare correttamente il valore e impostare il validatore locale per il tuo campo.
  2. Se il campo è serializzabile, imposta la proprietà corrispondente nel costruttore. I campi modificabili devono essere serializzabili e i campi sono modificabili per impostazione predefinita, quindi probabilmente devi impostare questa proprietà su true, a meno che tu non sappia che non deve essere serializzabile.
  3. (Facoltativo) Applica un'ulteriore personalizzazione (ad esempio, i campi etichetta consentono di passare una classe CSS, che viene poi applicata al testo).

JSON e registrazione

Nelle definizioni del blocco JSON, i campi sono descritti da una stringa (ad es. field_number, field_textinput). Blockly gestisce una mappa da queste stringhe agli oggetti campo e chiama fromJson sull'oggetto appropriato durante la costruzione.

Chiama Blockly.fieldRegistry.register per aggiungere il tipo di campo a questa mappa, passando la classe del campo come secondo argomento:

Blockly.fieldRegistry.register('field_generic', GenericField);

Devi anche definire la funzione fromJson. L'implementazione deve prima dereferenziare tutti i riferimenti ai token di localizzazione utilizzando replaceMessageReferences e poi passare i valori al costruttore.

GenericField.fromJson = function(options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
      options['value']);
  return new CustomFields.GenericField(value);
};

Inizializzazione in corso

Quando viene creato, il campo contiene solo un valore. L'inizializzazione è il punto in cui viene creato il DOM, il modello (se il campo ne possiede uno) e vengono associati gli eventi.

Display On-Block

Durante l'inizializzazione, sei responsabile della creazione di tutto ciò che ti servirà per la visualizzazione sul blocco del campo.

Valori predefiniti, sfondo e testo

La funzione initView predefinita crea un elemento rect di colore chiaro e un elemento text. Se vuoi che il campo abbia entrambi, oltre ad alcuni vantaggi aggiuntivi, chiama la funzione della superclasse initView prima di aggiungere il resto degli elementi DOM. Se vuoi che il campo contenga uno solo di questi elementi, puoi utilizzare le funzioni createBorderRect_ o createTextElement_.

Personalizzazione della costruzione del DOM

Se il campo è un campo di testo generico (ad es. Text Input), la costruzione del DOM verrà gestita automaticamente. In caso contrario, dovrai eseguire l'override della funzione initView per creare gli elementi DOM che ti serviranno durante il rendering futuro del campo.

Ad esempio, un campo a discesa può contenere sia immagini che testo. In initView crea un singolo elemento immagine e un singolo elemento di testo. Durante render_ mostra l'elemento attivo e nasconde l'altro, in base al tipo di opzione selezionata.

La creazione di elementi DOM può essere eseguita utilizzando il metodo Blockly.utils.dom.createSvgElement o i metodi tradizionali di creazione DOM.

I requisiti per la visualizzazione in blocco di un campo sono:

  • Tutti gli elementi DOM devono essere elementi secondari di fieldGroup_ del campo. Il gruppo di campi viene creato automaticamente.
  • Tutti gli elementi DOM devono rimanere all'interno delle dimensioni segnalate del campo.

Per ulteriori dettagli sulla personalizzazione e l'aggiornamento della visualizzazione sul blocco, consulta la sezione Rendering.

Aggiungere simboli di testo

Se vuoi aggiungere simboli al testo di un campo (ad esempio il simbolo di grado del campo Angolo), puoi aggiungere l'elemento simbolo (di solito contenuto in un <tspan>) direttamente al textElement_ del campo.

Eventi di input

Per impostazione predefinita, i campi registrano eventi di descrizione comando ed eventi mousedown (da utilizzare per mostrare gli editor). Se vuoi ascoltare altri tipi di eventi (ad esempio se vuoi gestire il trascinamento in un campo), devi eseguire l'override della funzione bindEvents_ del campo.

bindEvents_() {
  // Call the superclass function to preserve the default behavior as well.
  super.bindEvents_();

  // Then register your own additional event listeners.
  this.mouseDownWrapper_ =
  Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
      function(event) {
        this.originalMouseX_ = event.clientX;
        this.isMouseDown_ = true;
        this.originalValue_ = this.getValue();
        event.stopPropagation();
      }
  );
  this.mouseMoveWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
      function(event) {
        if (!this.isMouseDown_) {
          return;
        }
        var delta = event.clientX - this.originalMouseX_;
        this.setValue(this.originalValue_ + delta);
      }
  );
  this.mouseUpWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
      function(_event) {
        this.isMouseDown_ = false;
      }
  );
}

Per eseguire il binding a un evento, in genere devi utilizzare la funzione Blockly.utils.browserEvents.conditionalBind. Questo metodo di associazione degli eventi filtra i tocchi secondari durante i trascinamenti. Se vuoi che il gestore venga eseguito anche a metà di un trascinamento in corso, puoi utilizzare la funzione Blockly.browserEvents.bind.

Smaltimento

Se hai registrato eventuali listener di eventi personalizzati all'interno della funzione bindEvents_ del campo, dovrai annullarne la registrazione all'interno della funzione dispose.

Se hai inizializzato correttamente la visualizzazione del campo (aggiungendo tutti gli elementi DOM a fieldGroup_), il DOM del campo verrà eliminato automaticamente.

Gestione dei valori

→ Per informazioni sul valore di un campo rispetto al relativo testo, consulta Anatomia di un campo.

Ordine di convalida

Diagramma di flusso che descrive l&#39;ordine in cui vengono eseguiti i validatori

Implementazione di un validatore di classi

I campi devono accettare solo determinati valori. Ad esempio, i campi numerici devono accettare solo numeri, i campi colore solo colori e così via. Ciò è garantito tramite i validatori di classe e locali. Il convalidatore della classe segue le stesse regole dei convalidatori locali, tranne per il fatto che viene eseguito anche nel costruttore e, pertanto, non deve fare riferimento al blocco di origine.

Per implementare il validator della classe del campo, esegui l'override della funzione doClassValidation_.

doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

Gestione dei valori validi

Se il valore passato a un campo con setValue è valido, riceverai un callback doValueUpdate_. Per impostazione predefinita, la funzione doValueUpdate_:

  • Imposta la proprietà value_ su newValue.
  • Imposta la proprietà isDirty_ su true.

Se devi semplicemente memorizzare il valore e non vuoi eseguire alcuna gestione personalizzata, non devi eseguire l'override di doValueUpdate_.

In caso contrario, se vuoi:

  • Spazio di archiviazione personalizzato di newValue.
  • Modifica altre proprietà in base a newValue.
  • Salva se il valore corrente è valido o meno.

Dovrai eseguire l'override di doValueUpdate_:

doValueUpdate_(newValue) {
  super.doValueUpdate_(newValue);
  this.displayValue_ = newValue;
  this.isValueValid_ = true;
}

Gestione dei valori non validi

Se il valore passato al campo con setValue non è valido, riceverai un callback doValueInvalid_. Per impostazione predefinita, la funzione doValueInvalid_ non fa nulla. Ciò significa che per impostazione predefinita i valori non validi non verranno visualizzati. Inoltre, il campo non verrà sottoposto a rendering perché la proprietà isDirty_ non verrà impostata.

Se vuoi visualizzare valori non validi, devi ignorare doValueInvalid_. Nella maggior parte dei casi, devi impostare una proprietà displayValue_ sul valore non valido, impostare isDirty_ su true e override render_ per la visualizzazione nel blocco in modo che si aggiorni in base a displayValue_ anziché a value_.

doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}

Valori in più parti

Quando il campo contiene un valore composto da più parti (ad es. elenchi, vettori, oggetti), potresti voler che le parti vengano gestite come valori singoli.

doClassValidation_(newValue) {
  if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    this.cachedValidatedValue_ = newValue;
    return null;
  }
  return newValue;
}

Nell'esempio precedente, ogni proprietà di newValue viene convalidata singolarmente. Poi, alla fine della funzione doClassValidation_, se una proprietà individuale non è valida, il valore viene memorizzato nella cache della proprietà cacheValidatedValue_ prima di restituire null (non valido). La memorizzazione nella cache dell'oggetto con proprietà convalidate singolarmente consente alla funzione doValueInvalid_ di gestirle separatamente, semplicemente eseguendo un controllo !this.cacheValidatedValue_.property, anziché convalidare nuovamente ogni proprietà singolarmente.

Questo pattern per la convalida di valori in più parti può essere utilizzato anche nei validatori locali, ma al momento non è possibile applicarlo.

isDirty_

isDirty_ è un flag utilizzato nella funzione setValue, nonché in altre parti del campo, per indicare se il campo deve essere nuovamente sottoposto a rendering. Se il valore visualizzato del campo è cambiato, isDirty_ deve in genere essere impostato su true.

Testo

→ Per informazioni su dove viene utilizzato il testo di un campo e in che modo differisce dal valore del campo, consulta Anatomia di un campo.

Se il testo del campo è diverso dal valore del campo, devi ignorare la funzione getText per fornire il testo corretto.

getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}

Creazione di un editor

Se definisci la funzione showEditor_, Blockly ascolterà automaticamente i clic e chiamerà showEditor_ al momento opportuno. Puoi visualizzare qualsiasi HTML nell'editor racchiudendolo in uno dei due div speciali, chiamati DropDownDiv e WidgetDiv, che fluttuano sopra il resto dell'interfaccia utente di Blockly.

DropDownDiv viene utilizzato per fornire editor che si trovano all'interno di una casella collegata a un campo. Si posiziona automaticamente vicino al campo rimanendo entro i limiti visibili. Il selettore dell'angolo e il selettore colori sono buoni esempi di DropDownDiv.

Immagine del selettore di angolazione

WidgetDiv viene utilizzato per fornire editor che non si trovano all'interno di una casella. I campi numerici utilizzano WidgetDiv per coprire il campo con una casella di input di testo HTML. Mentre DropDownDiv gestisce il posizionamento per te, WidgetDiv no. Gli elementi dovranno essere posizionati manualmente. Il sistema di coordinate è in coordinate pixel relative all'angolo in alto a sinistra della finestra. L'editor di input di testo è un buon esempio di WidgetDiv.

Immagine dell&#39;editor di input di testo

showEditor_() {
  // Create the widget HTML
  this.editor_ = this.dropdownCreate_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // Set the dropdown's background colour.
  // This can be used to make it match the colour of the field.
  Blockly.DropDownDiv.setColour('white', 'silver');

  // Show it next to the field. Always pass a dispose function.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.disposeWidget_.bind(this));
}

Codice di esempio WidgetDiv

showEditor_() {
  // Show the div. This automatically closes the dropdown if it is open.
  // Always pass a dispose function.
  Blockly.WidgetDiv.show(
    this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));

  // Create the widget HTML.
  var widget = this.createWidget_();
  Blockly.WidgetDiv.getDiv().appendChild(widget);
}

Pulizia

Sia DropDownDiv che WidgetDiv gestiscono l'eliminazione degli elementi HTML del widget, ma devi eliminare manualmente tutti i listener di eventi che hai applicato a questi elementi.

widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}

La funzione dispose viene chiamata in un contesto null su DropDownDiv. Su WidgetDiv viene chiamato nel contesto di WidgetDiv. In entrambi i casi, è consigliabile utilizzare la funzione bind quando passi una funzione di eliminazione, come mostrato negli esempi DropDownDiv e WidgetDiv riportati sopra.

→ Per informazioni sullo smaltimento non specifiche per lo smaltimento degli editor, vedi Smaltimento.

Aggiornamento della visualizzazione sul blocco

La funzione render_ viene utilizzata per aggiornare la visualizzazione in blocco del campo in modo che corrisponda al suo valore interno.

Ecco alcuni esempi comuni:

  • Modificare il testo (menu a discesa)
  • Cambiare il colore (colore)

Impostazioni predefinite

La funzione render_ predefinita imposta il testo visualizzato sul risultato della funzione getDisplayText_. La funzione getDisplayText_ restituisce la proprietà value_ del campo convertita in una stringa, dopo essere stata troncata per rispettare la lunghezza massima del testo.

Se utilizzi la visualizzazione predefinita sul blocco e il comportamento predefinito del testo funziona per il tuo campo, non è necessario eseguire l'override di render_.

Se il comportamento predefinito del testo funziona per il tuo campo, ma la visualizzazione on-block del campo presenta elementi statici aggiuntivi, puoi chiamare la funzione render_ predefinita, ma dovrai comunque eseguirne l'override per aggiornare le dimensioni del campo.

Se il comportamento predefinito del testo non funziona per il tuo campo o se la visualizzazione on-block del campo presenta elementi dinamici aggiuntivi, dovrai personalizzare la render_ funzione.

Diagramma di flusso che descrive come decidere se eseguire l&#39;override di render_

Personalizzazione del rendering

Se il comportamento di rendering predefinito non funziona per il tuo campo, dovrai definire un comportamento di rendering personalizzato. Puoi impostare un testo di visualizzazione personalizzato, modificare gli elementi delle immagini o aggiornare i colori di sfondo.

Tutte le modifiche agli attributi DOM sono legali. Le uniche due cose da ricordare sono:

  1. La creazione del DOM deve essere gestita durante l'inizializzazione, in quanto è più efficiente.
  2. Devi sempre aggiornare la proprietà size_ in modo che corrisponda alle dimensioni della visualizzazione nel blocco.
render_() {
  switch(this.value_.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      break;
    case 'Crown':
      this.crown_.style.display = '';
      break;
    case 'Mask':
      this.mask_.style.display = '';
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      break;
  }

  switch(this.value_.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  this.textContent_.nodeValue = this.value_.turtleName;

  this.updateSize_();
}

Aggiornamento delle dimensioni

L'aggiornamento della proprietà size_ di un campo è molto importante, in quanto indica al codice di rendering del blocco come posizionare il campo. Il modo migliore per capire esattamente cosa dovrebbe essere size_ è sperimentare.

updateSize_() {
  const bbox = this.movableGroup_.getBBox();
  let width = bbox.width;
  let height = bbox.height;
  if (this.borderRect_) {
    width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
}

Colori dei blocchi corrispondenti

Se vuoi che gli elementi del campo corrispondano ai colori del blocco a cui sono collegati, devi eseguire l'override del metodo applyColour. Per accedere al colore, devi utilizzare la proprietà style del blocco.

applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}

Aggiornamento della modificabilità

La funzione updateEditable può essere utilizzata per modificare l'aspetto del campo a seconda che sia modificabile o meno. La funzione predefinita fa in modo che lo sfondo abbia o meno una risposta al passaggio del mouse (bordo) se è modificabile o meno. La visualizzazione nel blocco non deve cambiare dimensione a seconda della sua modificabilità, ma tutte le altre modifiche sono consentite.

updateEditable() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  super.updateEditable();

  const group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
}

Serializzazione

La serializzazione consiste nel salvare lo stato del campo in modo che possa essere ricaricato nello spazio di lavoro in un secondo momento.

Lo stato dello spazio di lavoro include sempre il valore del campo, ma potrebbe includere anche altri stati, ad esempio lo stato dell'interfaccia utente del campo. Ad esempio, se il campo era una mappa su cui era possibile ingrandire e rimpicciolire che consentiva all'utente di selezionare i paesi, potevi serializzare anche il livello di zoom.

Se il campo è serializzabile, devi impostare la proprietà SERIALIZABLE su true.

Blockly fornisce due set di hook di serializzazione per i campi. Una coppia di hook funziona con il nuovo sistema di serializzazione JSON, mentre l'altra coppia funziona con il vecchio sistema di serializzazione XML.

saveState e loadState

saveState e loadState sono hook di serializzazione che funzionano con il nuovo sistema di serializzazione JSON.

In alcuni casi non è necessario fornirli, perché le implementazioni predefinite funzioneranno. Se (1) il tuo campo è una sottoclasse diretta della classe Blockly.Field di base, (2) il tuo valore è un tipo serializzabile JSON e (3) devi solo serializzare il valore, l'implementazione predefinita funzionerà perfettamente.

In caso contrario, la funzione saveState deve restituire un oggetto/valore serializzabile JSON che rappresenta lo stato del campo. La funzione loadState deve accettare lo stesso oggetto/valore serializzabile JSON e applicarlo al campo.

saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}

Serializzazione completa e dati di backup

saveState riceve anche un parametro facoltativo doFullSerialization. Questo viene utilizzato dai campi che normalmente fanno riferimento allo stato serializzato da un serializzatore diverso (come i modelli di dati di supporto). Il parametro indica che lo stato a cui viene fatto riferimento non sarà disponibile quando il blocco viene deserializzato, quindi il campo deve eseguire autonomamente tutta la serializzazione. Ad esempio, questo è vero quando un singolo blocco viene serializzato o quando un blocco viene copiato e incollato.

Due casi d'uso comuni sono:

  • Quando un singolo blocco viene caricato in uno spazio di lavoro in cui non esiste il modello di dati sottostante, il campo dispone di informazioni sufficienti nel proprio stato per creare un nuovo modello di dati.
  • Quando un blocco viene copiato e incollato, il campo crea sempre un nuovo modello di dati di supporto anziché fare riferimento a uno esistente.

Un campo che lo utilizza è il campo della variabile integrata. Normalmente serializza l'ID della variabile a cui fa riferimento, ma se doFullSerialization è true serializza tutto il suo stato.

saveState(doFullSerialization) {
  const state = {'id': this.variable_.getId()};
  if (doFullSerialization) {
    state['name'] = this.variable_.name;
    state['type'] = this.variable_.type;
  }
  return state;
}

loadState(state) {
  const variable = Blockly.Variables.getOrCreateVariablePackage(
      this.getSourceBlock().workspace,
      state['id'],
      state['name'],   // May not exist.
      state['type']);  // May not exist.
  this.setValue(variable.getId());
}

Il campo della variabile esegue questa operazione per assicurarsi che, se viene caricato in un workspace in cui la variabile non esiste, possa creare una nuova variabile a cui fare riferimento.

toXml e fromXml

toXml e fromXml sono hook di serializzazione che funzionano con il vecchio sistema di serializzazione XML. Utilizza questi hook solo se necessario (ad es. se stai lavorando su un codebase precedente che non è ancora stato migrato), altrimenti utilizza saveState e loadState.

La funzione toXml deve restituire un nodo XML che rappresenta lo stato del campo. La funzione fromXml deve accettare lo stesso nodo XML e applicarlo al campo.

toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}

Proprietà modificabili e serializzabili

La proprietà EDITABLE determina se il campo deve avere un'interfaccia utente per indicare che è possibile interagire con esso. Il valore predefinito è true.

La proprietà SERIALIZABLE determina se il campo deve essere serializzato. Il valore predefinito è false. Se questa proprietà è true, potresti dover fornire funzioni di serializzazione e deserializzazione (vedi Serializzazione).

Personalizzazione con CSS

Puoi personalizzare il campo con CSS. Nel metodo initView, aggiungi una classe personalizzata a fieldGroup_ del campo, poi fai riferimento a questa classe nel CSS.

Ad esempio, per utilizzare un cursore diverso:

initView() {
  ...

  // Add a custom CSS class.
  if (this.fieldGroup_) {
    Blockly.utils.dom.addClass(this.fieldGroup_, 'myCustomField');
  }
}
.myCustomField {
  cursor: cell;
}

Personalizzare il cursore

Per impostazione predefinita, le classi che estendono FieldInput utilizzano un cursore text quando un utente passa il mouse sopra il campo, i campi trascinati utilizzano un cursore grabbing e tutti gli altri campi utilizzano un cursore default. Se vuoi utilizzare un cursore diverso, impostalo utilizzando CSS.