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:
- Implementa un costruttore.
- Registra una chiave JSON e implementa
fromJson
. - Gestisci l'inizializzazione dell'interfaccia utente e dei listener di eventi on-block.
- Gestisci l'eliminazione dei listener di eventi (l'eliminazione della UI viene gestita per te).
- Implementa la gestione dei valori.
- Aggiungi una rappresentazione testuale del valore del campo per l'accessibilità.
- Aggiungi funzionalità aggiuntive, ad esempio:
- 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:
- 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. - 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.
- (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
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_
sunewValue
. - Imposta la proprietà
isDirty_
sutrue
.
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 e WidgetDiv
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
.
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
.
Codice di esempio DropDownDiv
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.
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:
- La creazione del DOM deve essere gestita durante l'inizializzazione, in quanto è più efficiente.
- 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.