Note: Il existe des versions obsolètes de ce composant. Passez à une version de la documentation antérieure à la version 10.0.x pour consulter cette documentation.
Le composant Liste déroulante propose aux utilisateurs un ensemble d'options parmi lesquelles ils peuvent effectuer une sélection. Les listes déroulantes affichent des options au fur et à mesure, ce qui empêche les utilisateurs de voir trop d'informations en même temps. Elles peuvent également être utilisées pour regrouper des informations. Les listes déroulantes sont toujours accompagnées d'une étiquette ou d'un titre de champ positionné avant le contrôle.
Ce composant se décline en quatre variantes : select, multiple select, combobox et multiple combobox.
La variante combobox multiple de la liste déroulante permet aux utilisateurs de filtrer les listes plus longues pour qu'elles ne contiennent que les sélections correspondant à une requête. L’utilisateur peut sélectionner plusieurs options dans la liste.
Si la liste des sélections n'est pas suffisamment complexe pour une variante combobox, vous pouvez utiliser la liste déroulante multiple select.
Il existe un petit nombre d'options (5 ou moins) parmi lesquelles les utilisateurs peuvent choisir. Utilisez des cases à cocher pour les éléments à sélection multiple.
Structure du composant liste déroulante, variante combobox multiple
Étiquette : décrit l'objectif du champ de saisie.
Texte d'aide (facultatif) : donne un contexte supplémentaire ou aide l'utilisateur à faire le bon choix.
Entrée de liste déroulante : affiche les options sélectionnées par l'utilisateur. Les utilisateurs peuvent saisir des données dans le champ de saisie pour trouver une option qui correspond à leur requête.
Menu déroulant : affiche une liste d'options parmi lesquelles choisir.
Jutro comporte des menus déroulants avec des champs de saisie semi-automatique. Les utilisateurs peuvent saisir manuellement du texte pour filtrer les éléments de la liste déroulante. Cette fonctionnalité permet d'aider les utilisateurs à effectuer une sélection dans une longue liste.
Une liste déroulante dont l'option de saisie semi-automatique est activée présente des suggestions lorsque l'utilisateur commence à saisir du texte. Les sélections apparaissent en fonction des caractères saisis par l'utilisateur. Plus les utilisateurs saisissent de caractères dans le champ, plus la liste s'affine.
Lorsque vous utilisez la liste déroulante à des fins de tri, pensez à organiser les options dans l'ordre. Par exemple :
Option de la plus courante à la moins courante
Opération de la plus simple à la plus complexe
De l'option la moins risquée à l'option la plus risquée
La première option apparaît en haut de la liste.
Évitez de classer les options par ordre alphabétique sauf si cela a du sens pour votre cas d'utilisation. Une liste de pays ou autres problèmes d'éléments connus peuvent souvent être classés par ordre alphabétique. Cependant, vous devez vous assurer que les utilisateurs verront le nom de leur sélection sans ambiguïté.
Mettez une majuscule en début de phrase pour tous les aspects relatifs à la conception des interfaces de produit Guidewire. Ne mettez pas la 1re lettre du 1er mot du titre en majuscule.
Utilisez des verbes au présent et la forme active dans la plupart des cas.
Placez l'étiquette de la liste déroulante en dehors du champ de façon à ce qu'elle soit toujours visible. Une liste déroulante sans étiquette n'est pas accessible.
Ne remplacez pas les étiquettes de champ par du texte générique dans le champ. Cela nuit à la convivialité et a de nombreuses conséquences négatives.
Placez une étiquette permanente en dehors du champ.
N'utilisez pas de texte de l'espace réservé pour remplacer l'étiquette d'un champ.
Utilisez du texte d'aide pour afficher le contexte et indiquer ce qu'il faut sélectionner ou comment sélectionner une option. Voici quelques exemples de ce que vous pouvez inclure dans le texte d'aide :
Une description générale des options de liste déroulante
Des astuces pour aider l'utilisateur à effectuer la bonne sélection
Plus de contexte pour expliquer pourquoi un utilisateur doit effectuer une sélection
N'utilisez le texte d'aide que pour les informations pertinentes. Évitez d'utiliser un texte d'aide qui reformule simplement les mêmes informations que celles qui apparaissent dans l'étiquette.
Mettez la première lettre de la phrase en majuscule pour le texte d'aide. Rédigez 1 à 2 phrases courtes et complètes qui se terminent par un point.
N'utilisez pas de texte d'aide pour indiquer le contexte.
N'utilisez pas le texte d'aide pour simplement reformuler les mêmes informations que celles qui apparaissent dans l'étiquette.
Ne mettez pas de texte d'espace réservé dans le champ de saisie de texte. Un texte d'espace réservé sollicite la mémoire à court terme des utilisateurs, car il disparaît une fois qu'une valeur est entrée. Il représente également un fardeau supplémentaire pour les utilisateurs souffrant de troubles visuels et cognitifs.
Placez plutôt les conseils et les instructions en dehors du champ.
Le champ de saisie des combobox multiples suit les instructions relatives au contenu des champs de texte.
Placez les conseils et les instructions en dehors du champ.
N'ajoutez pas d'espaces réservés au champ de saisie de texte.
Utiliser un texte d'erreur pour guider les utilisateurs
Le texte du message d'erreur indique à l'utilisateur comment corriger l'erreur. Dans le cas de la liste déroulante, les erreurs sont souvent liées à un problème qui doit être résolu pour la validation intégrée. Par exemple, si quelqu’un ne sélectionne pas de facteur de risque et que ce champ est obligatoire, vous pouvez utiliser un texte d’erreur pour guider l’utilisateur vers une solution : « Sélectionnez un ou plusieurs facteurs de risque ».
Mettez la 1re lettre du 1er mot en majuscule pour le texte d'erreur. Rédigez 1 à 2 phrases courtes et complètes qui se terminent par un point.
Utilisez le texte d'erreur pour guider l'utilisateur et lui proposer une solution.
Ne rédigez pas de messages d'erreur ambigus ou ne laissez pas les utilisateurs deviner comment résoudre un problème.
Utilisez un astérisque (*) pour indiquer les champs obligatoires. L'astérisque précède l'étiquette du champ. Cela permet aux utilisateurs de localiser facilement les champs devant obligatoirement être renseignés en regardant uniquement le caractère le plus à gauche de l'étiquette.
En plus de marquer les champs obligatoires par un astérisque, il est recommandé d'inclure des instructions claires en haut du formulaire, telles que « Tous les champs marqués d'un astérisque sont obligatoires », afin de vous assurer que les utilisateurs comprennent la signification de l'astérisque.
Utilisez un astérisque pour signaler qu'un champ est obligatoire.
N'utilisez pas d'astérisque pour signaler quelque chose qui est facultatif.
La variante « combobox multiple » apparaît sans valeur (par défaut), sans texte fictif ou sans saisie.
Visuel
État
Description
Aucune valeur (par défaut)
Indique à l'utilisateur qu'aucune valeur n'a été sélectionnée et qu'il n'y a pas d'espace réservé.
Espace réservé
Indique à l'utilisateur qu'aucune valeur n'a été sélectionnée. L'espace réservé est grisé.
Entrée renseignée
Indique à l'utilisateur que l'entrée est renseignée.
La combobox multiple a également des états interactifs pour activé, focus, désactivé, erreur, lecture seule et affichage uniquement.
État
Description
Activé
Indique à l'utilisateur que l'élément est prêt à interagir.
Focus
Indique à l'utilisateur l'élément de l'interface utilisateur sur lequel le système est centré.
Désactivé
Indique à l'utilisateur que la valeur de saisie ne peut pas être modifiée en raison de facteurs locaux. Par exemple, une case à cocher située au-dessus du champ de saisie doit être cochée pour accéder à ce champ de saisie. Un utilisateur peut effectuer une action pour l'activer en interagissant avec la page.
Erreur
Indique que l'utilisateur a commis une erreur de validation. Le texte d'erreur permet aux utilisateurs d'apporter une correction.
Lecture seule
Indique à l'utilisateur que la valeur de saisie ne peut pas être modifiée en raison de facteurs externes. Par exemple, pas d'accès en écriture. Un utilisateur peut effectuer une action pour l'activer, par exemple en contactant un administrateur.
Affichage uniquement
L'état Affichage uniquement est utilisé dans deux cas :
Un élément d'interface utilisateur est utilisé en mode affichage.
Un élément d'interface utilisateur est affiché en mode édition, mais il n'est jamais modifiable.
Cet état était auparavant appelé « lecture seule ».
L'image suivante illustre les états interactifs de la combobox multiple.
La liste déroulante est développée et réduite à l'aide de la barre d'espace. Les utilisateurs naviguent parmi les options à l'aide des touches fléchées et les sélectionnent à l'aide de la barre d'espace ou de la touche Entrée.
aria-labelledby crée une association programmatique entre le champ de saisie et son étiquette. L'attribut WAI-ARIA de aria-autocomplete='list' informe qu'une liste dans laquelle l'utilisateur peut faire un choix apparaîtra, mais la zone de modification reste active. La sélection de l'option « obligatoire » dans Storybook ajoute les attributs « required » et « aria-required="true" » au champ de saisie.
Ce composant a été validé pour répondre aux directives d’accessibilité WCAG 2.2 AA dans sa configuration de base par défaut. Il s’agit notamment de s’assurer que :
Le rapport de contraste des éléments textuels par rapport à leur arrière-plan est supérieur à 4.5:1.
Le contenu non textuel qui doit transmettre le sens (comme les icônes et les indicateurs de focus) a un rapport de contraste d’au moins 3:1 avec ses couleurs adjacentes.
Cet élément peut être utilisé à l’aide d’un clavier et d’une souris.
Le contenu est accessible à l’aide de lecteurs d’écran, tels que JAWS et VoiceOver.
La conformité de l’accessibilité dépend en définitive de la façon dont ce composant est implémenté et personnalisé. Les modifications apportées par l’auteur du contenu peuvent affecter l’accessibilité. Pour en savoir plus sur notre modèle de responsabilité partagée, consultez notre Déclaration d’accessibilité Jutro complète.
Lorsque vous utilisez ce composant dans votre application :
Facilitez la compréhension du contenu, pour les utilisateurs, en évitant les noms d'option très longs.
Évitez d'utiliser des éléments totalement focalisables tels que des boutons, des cases à cocher et des liens, ou du contenu totalement sémantique, tel que des titres, dans vos composants de liste déroulante.
Note: Il existe des versions obsolètes de ce composant. Passez à une version de la documentation antérieure à la version 10.0.x pour consulter cette documentation.
Assurez-vous de comprendre la surface de l'API des composants du système de conception, ainsi que les implications et les compromis. Pour en savoir plus, reportez-vous à notre introduction à l'API des composants.
Warning: La transmission d'options enfant au composant MultipleSelect autre que SelectOption n'est pas prise en charge. Bien que cela puisse fonctionner, des comportements inattendus peuvent en résulter ou la transmission peut être affectée par des modifications introduites dans des versions ultérieures.
If set to true, adds an icon button that appears when at least one option is selected, or any text is typed. It allows the user to unselect all selected options and delete the typed text at once.
If set to true, the tags are displayed permanently in expanded view. This overrides the default behavior where excess tags collapse into a tag containing aggregated options.
Value of the component, in the form of an array of currently selected options. Takes precedence over initialValue. If this prop is passed, component works in controlled mode and its value will change only if this prop changes.
Le crochet useFilteredOptions offre les mécanismes qui permettent de gérer les options affichées en fonction de l'entrée utilisateur.
Paramètres reçus :
initialOptions: TValue[]. Liste initiale des options dans le composant sans filtre appliqué
Résultat :
Un objet avec :
filteredOptions: TValue[]. Série avec les options résultant de l'application du filtre
onSearch: function. Fonction qui récupère l'événement d'entrée onSearch et filtre les options en fonction de celui-ci. Cela peut être attribué à l'événement onSearch du composant.
resetFilter: function. Utilisé pour réinitialiser les options de filtre, définissant la série filteredOptions sur sa valeur initiale
Lorsque les balises des options sélectionnées dépassent l’espace du composant, certaines d’entre elles se réduisent en une seule balise qui indique le nombre d’options sélectionnées qui ne sont pas affichées. Lorsque vous cliquez sur cette balise, le composant se développe verticalement pour afficher toutes les balises des options sélectionnées. Une fois développées, les balises d’options ne peuvent pas être réduites.
Pour remplacer ce comportement et afficher toutes les balises d’option de façon permanente dans une vue développée, définissez la propriété tagsAlwaysExpanded sur true. Pour comparer les deux comportements, reportez-vous aux exemples.
Tous les composants Liste déroulante définissent la liste des options disponibles via la propriété children de type ReactNode. Vous pouvez utiliser uniquement les sous-composants suivants comme enfants :
SelectOption utilisé par Select et MultipleSelect
ComboboxOption utilisé par Combobox et MultipleCombobox
Warning:
SelectOption et ComboboxOption doivent uniquement être utilisés dans le contexte de leur composant « parent » respectif. Bien qu'ils puissent fonctionner individuellement, cela n'est pas pris en charge.
Les composants Select et MultipleSelect sont conçus uniquement pour accepter les composants SelectOption en tant qu'enfants. Bien qu'ils puissent afficher ou gérer d'autre types ou éléments HTML, cela n'est pas pris en charge.
Les composants Combobox et MultipleCombobox sont conçus uniquement pour accepter les composants ComboboxOption en tant qu'enfants. Bien qu'ils puissent afficher ou gérer d'autre types ou éléments HTML, cela n'est pas pris en charge.
Toute utilisation de ces composants en dehors de leur champ d'application pris en charge tombe en dehors de la validation de modifications majeures car une telle utilisation n'est pas considérée comme faisant partie du cahier des charges des composants. Ces utilisations peuvent entraîner des comportements inattendus ou être affectées par des modifications introduites dans des versions ultérieures.
Les entrées Jutro ont implémenté des gestionnaires impératifs en tant que mécanisme permettant d'accéder à certaines fonctionnalités natives communes susceptibles de vous être utiles. Les fonctionnalités suivantes sont disponibles :
Définir le focus pour vous permettre de définir le focus utilisateur dans un composant spécifique
Flou pour supprimer le focus du composant
Faire défiler l'écran jusqu'au composant pour pouvoir amener l'utilisateur à une zone spécifique de la page.
Ces fonctionnalités sont fournies via la propriété ref, qui les expose comme suit :
Bien que certains composants de Jutro puissent fournir des fonctionnalités complémentaires ou une fonction d'aide pour faciliter le processus de validation, il vous appartient, en tant que développeur, de gérer la validation de toute entrée utilisateur (en utilisant ou non les fonctions d'aide complémentaires) et de déterminer les messages d'erreur à afficher.
Le comportement des composants Jutro est basé sur l'implémentation du développeur.
Quand est-ce que des messages d'erreur s'affichent ?
Les messages d'erreur ne s'affichent que lorsque vous les transmettez au composant via la propriété stateMessages. Cette propriété reçoit un objet avec le contenu suivant :
Le composant affiche chaque message d'erreur fourni dans le même ordre que dans la série.
Quand la validation a-t-elle lieu ?
Cette décision vous revient en tant que développeur. Étant donné que les composants ne déterminent pas le moment où la validation est effectuée ou le moment où l'erreur doit être affichée, vous devez implémenter la logique pour la traiter en fonction des exigences du projet, par exemple lorsque l'utilisateur modifie le contenu, lorsque le composant active une autre fenêtre et lors de la soumission du formulaire.
Note: Il existe des versions obsolètes de ce composant. Passez à une version de la documentation antérieure à la version 10.0.x pour consulter cette documentation.
Consultez la section Utilisation pour savoir comment et quand utiliser MultipleCombobox et les onglets spécifiques pour en savoir plus sur les fonctionnalités proposées par le composant.
Le crochet useFilteredOptions est utilisé pour implémenter le mécanisme de filtre en fonction de l'entrée utilisateur. Toutefois, pour des questions de clarté et de simplicité, il n'est pas inclus dans certains exemples. Vous trouverez des informations plus détaillées sur le crochet ici.
Note: La plupart des fonctionnalités et propriétés des quatre composants Liste déroulante fournis ne sont pas différentes dans leur utilisation. Certains exemples d'autres composants peuvent être pertinents pour MultipleCombobox également.
Vous pouvez désactiver complètement n'importe lequel des composants Liste déroulante en définissant la propriété disabled sur true. Vous pouvez également désactiver des options spécifiques à l'aide de la propriété disabled des composants SelectOption et ComboboxOption. Dans ce cas, l'ensemble du composant est désactivé et la propriété disabled des options n'a aucun effet.
Il s'agit d'un comportement commun à tous les composants Liste déroulante : lorsque la propriété value est définie, l'utilisateur ne peut pas modifier directement le composant ; il sera géré entièrement par l'implémentation du développeur.
Cet exemple est applicable à tous les composants Liste déroulante :
exportfunctionMultiComboControlled(){ const[updatedValue, setNewValue]=useState([]); constonChange=(e, newValue)=>{ setNewValue(newValue); }; const options =[ <ComboboxOption value={{ id:'1', label:'Option 1', }} />, <ComboboxOption value={{ id:'2', label:'Option 2', }} />, <ComboboxOption value={{ id:'3', label:'Option 3', }} />, <ComboboxOption value={{ id:'4', label:'Option 4', }} />, <ComboboxOption value={{ id:'5', label:'Option 5', }} />, ]; return( <div> <MultipleCombobox label="Choose values" secondaryLabel="This value will be passed to the list of options below" onChange={onChange}> {options} </MultipleCombobox> <br/> <MultipleCombobox label="Changes with the above" value={updatedValue}> {options} </MultipleCombobox> </div> ); }
Il est possible, à l'aide de la propriété ref et des gestionnaires impératifs fournis (« focus », « blur » et « scrollIntoView »), d'exécuter différentes actions natives. Définir le focus sur le composant en est un exemple.
Il s'agit d'un exemple utilisant le composant MultipleCombobox :
Les composants Liste déroulante ne gèrent pas le processus de validation, mais vous pouvez gérer le statut et les messages d'erreur à afficher à l'aide de la propriété stateMessages.
La logique des messages d'état fonctionne pour tous les composants Liste déroulante. Un exemple de MultipleSelect est illustré ci-dessous :
Le crochet useFilteredOptions fournit un traitement de base des données pour le filtrage.
importReact,{ useState }from'react'; import{IntlMessageShape}from'@jutro/prop-types'; import{ useTranslator }from'@jutro/locale'; type SelectValue={ id: string; label:IntlMessageShape; [index:PropertyKey]: unknown; }; /** * @typedef {Object} FilterHookReturnObject * @property {TValue[]} filteredOptions - array of filtered options * @property {function} onSearch - function that takes the onSearch input event * and filters the options based on it * @property {function} resetFilter - used to reset the filtering for options, * after using filteredOptions array resets to initial one * @property {function} onAddNew - function that adds new value to the internal * options array inside the hook and calls appendToSelection that is responsible * for selecting this new value inside the dropdown */ /** * Helper hook for filtering options based on user input. * * @param {TValue[]} initialOptions - initial list of options in component * @param {function} createNewValue - function for constructing value object * from string * @param {function} appendToSelection - callback that is responsible for * appending value to the controlled list of selected values * * @returns {FilterHookReturnObject} - helpers for filtering purposes: * filtered options array, onAddNew and onSearch function to be supplied * to component and resetFilter function */ exportconst useFilteredOptions =<TValueextendsSelectValue=SelectValue>( initialOptions:TValue[], createNewValue?:(value: string)=>TValue, appendToSelection?:(value:TValue)=>void ):{ filteredOptions:TValue[]; onSearch:(event:React.ChangeEvent<HTMLInputElement>) => void; resetFilter: () => void; onAddNew: (newValue: string) => void; } => { const translator =useTranslator(); const[query, setQuery]=useState(''); const[options, setOptions]=useState(initialOptions); // Basic filtering. This part should be changed to cover your needs. const filteredOptions = options.filter(val=> translator(val.label) .trim() .toLowerCase() .includes(query.trim().toLowerCase()) ); // Function that should be passed to the Combobox onSearch prop. // It gets the value of the search term inside Combobox. const onSearch =(event:React.ChangeEvent<HTMLInputElement>) => { setQuery(event.target.value); }; // Function that is responsible for clearing the query // only when the Combobox is on active browser tab. // Typically connected to Combobox onBlur. const resetFilter = () => { if(document.visibilityState==='visible'&&document.hasFocus()){ setQuery(''); } }; // Function responsible on adding new values to both // internal state of filtered items and external // controlled state value const onAddNew = (newValue: string) => { const valueTransformed = createNewValue?.(newValue); setQuery(''); if(valueTransformed){ // adds the value to the internal state setOptions(prevOptions=>[...prevOptions, valueTransformed]); // adds the value to the controlled state appendToSelection?.(valueTransformed); } }; return { filteredOptions, onSearch, resetFilter, onAddNew }; };
Exemple d'implémentation avec des appels d'API asynchrones
Les appels d'API asynchrones ne sont pas gérés à l'intérieur du crochet useFilteredOptions de base. Pour cela, vous devrez peut-être créer votre propre implémentation en fonction de vos besoins spécifiques. Voici un exemple de crochet personnalisé pour le composant MultipleCombobox :
importReact,{ useEffect, useState }from'react'; import{ComboboxOption,MultipleCombobox}from'@jutro/components'; import{ useTranslator }from'@jutro/locale'; import{IntlMessageShape}from'@jutro/prop-types'; type SelectValue={ id: string; label:IntlMessageShape; [index:PropertyKey]: unknown; }; constsomeAPICallback=asyncquery=>{ console.log('calling the API'); constCHARACTERS=[ {id:'1',label:'Anakin Skywalker'}, {id:'2',label:'Luke Skywalker'}, {id:'3',label:'Master Yoda'}, {id:'4',label:'Han Solo'}, ]; const result =newPromise(resolve=>{ setTimeout( ()=> resolve( CHARACTERS.filter(val=> val.label .trim() .toLowerCase() .includes(query.trim().toLowerCase()) ) ), 3000 ); }); return result; }; const useFilteredOptions =<TValueextendsSelectValue=SelectValue>():{ filteredOptions:TValue[]; onSearch:(event:React.ChangeEvent<HTMLInputElement>) => void; resetFilter: () => void; loading: boolean; } => { const[query, setQuery]=useState(''); const[filteredOptions, setFilteredOptions]=useState([]); const[loading, setLoading]=useState(true); useEffect(()=>{ setLoading(true); // This is a simple example only, the API callback should be debounced // to ensure the async operation does not overlap with the query change. someAPICallback(query).then((opts:TValue[])=>{ setFilteredOptions(opts); setLoading(false); }); },[query]); // Function that should be passed to the Combobox onSearch prop. // It gets the value of the search term inside Combobox. const onSearch =(event:React.ChangeEvent<HTMLInputElement>) => { setQuery(event.target.value); }; // Function that is responsible for clearing the query // only when the Combobox is on active browser tab. // Typically connected to Combobox onBlur. const resetFilter = () => { if(document.visibilityState==='visible'&&document.hasFocus()){ setQuery(''); } }; return { filteredOptions, onSearch, resetFilter, loading }; }; export const Welcome: React.FC = () => { const translator =useTranslator(); const{ filteredOptions, onSearch, resetFilter, loading }= useFilteredOptions(); return( <MultipleCombobox label="Select options" secondaryLabel="Options filtered by user input" onSearch={onSearch} onBlur={resetFilter} > {loading ?( <span>{translator('Loading...')}</span> ):( filteredOptions.map(({ id, label })=>( <ComboboxOptionkey={id}value={{ id, label }}/> )) )} </MultipleCombobox> ); };
Le comportement par défaut du composant MultipleCombobox consiste à réduire les étiquettes des options lorsqu’elles ne peuvent pas tenir sur une seule ligne. Le composant se développe en une vue multiligne lorsque vous cliquez sur la balise contenant les options agrégées. Ce comportement est contrôlé par la propriété tagsAlwaysExpanded et, par défaut, il est défini sur false.
Pour activer l’effacement de la saisie d’un simple clic sur le bouton d’icône, définissez la propriété clearable sur true. Le bouton apparaît lorsqu'au moins une option est sélectionnée ou qu’un texte est saisi. Il permet à l’utilisateur de désélectionner les options sélectionnées ou de supprimer le texte en une seule fois.
Ajout de la propriété tagsAlwaysExpanded qui, lorsqu’elle est définie sur true, remplace le comportement par défaut Balises d’option réduites.
Ajout de la propriété clearable, qui, lorsqu’elle est définie sur true, permet à l’utilisateur d’effacer les options sélectionnées ou de supprimer le texte saisi en cliquant simplement sur le bouton d'icône.
Nouveau composant @jutro/components/MultipleCombobox introduit.
Le composant précédent TypeaheadMultiSelectField est obsolète et a été déplacé vers le package @jutro/legacy. Pour afficher la documentation correspondante, passez à une version de la documentation antérieure à la version 10.0.