SetOfInspector
- Responder
- View
- Inspector
- SetOfInspector
- Inspector
- View
Cliquez dans une bande de couleur pour la sélectionner ou utilisez le bouton avec un triangle vers la gauche et le bouton avec un triangle vers la droite pour déplacer le sélecteur. La position de la bande sélectionnée et le nombre total de bandes sont affichés à droite des boutons. La couleur et la largeur de la bande sélectionnée sont éditées par un ColorInspector et un RangeInspector.
Cliquez dans le champ de saisie de la couleur. Choisissez une couleur. Appuyez sur Entrée ou cliquez en dehors du sélecteur pour valider la couleur choisie.
La bande sélectionnée change de couleur.
Entrez directement une valeur, e.g. #C80
.
Déplacez la boule de la glissière vers la gauche ou vers la droite pour changer la largeur de la bande sélectionnée. NOTE : La largeur d'une bande est comprise entre 10 et 60 px.
Utilisez le bouton avec une flèche vers la gauche et le bouton avec une flèche vers la droite pour déplacer la bande sélectionnée.
Cliquez sur le bouton plus pour ajouter une bande. NOTE : L'inspecteur est configuré pour autoriser un maximum de 6 bandes.
Cliquez sur le bouton moins pour retirer la bande sélectionnée. NOTE : L'inspecteur est configuré pour laisser un minimum de 2 bandes.
IMPORTANT : La disposition et le style d'une interface sont à la discrétion du programmeur. Aucun modèle graphique n'est imposé. Les programmes de test utilisent les icônes de Font Awesome. Tous les boutons de contrôle sont optionnels.
Dans la console du navigateur, verrouillez toute l'interface en bloquant l'inspecteur :
tester.disable()
Déverrouillez l'interface :
tester.enable()
Affichez la valeur de l'inspecteur :
inspector.get()
[ {"color": "#541743", "width": 20}, {"color": "#8C0C3C", "width": 30}, ... ]
- function SetOfInspector(inspector, options = false) {
- if (!( inspector instanceof Inspector))
- throw new TypeError();
- options = options || {};
- let defaultItem = options.defaultItem;
- let min = options.min;
- let max = options.max;
- if (! (typeof min === 'undefined' || Number.isInteger(min)))
- throw new TypeError();
- if (! (typeof max === 'undefined' || Number.isInteger(max)))
- throw new TypeError();
- if (typeof min === 'number' && min < 1)
- throw new RangeError();
- if (typeof max === 'number' && max < 1)
- throw new RangeError();
- if (typeof min === 'number' && typeof max === 'number' && min > max)
- throw new RangeError();
- Inspector.call(this);
- inspector.addNextResponder(this);
- this._inspector = inspector;
- this._defaultItem = defaultItem;
- this._min = min;
- this._max = max;
- this._pos = 1;
- this._value = [];
- }
- SetOfInspector.prototype = Object.create(Inspector.prototype);
- Object.defineProperty(SetOfInspector.prototype, 'constructor', { value: SetOfInspector, enumerable: false, writable: true });
- Object.defineProperty(SetOfInspector.prototype, 'itemIndex', {
- get: function() {
- return this._pos;
- },
- set: function(pos) {
- if (!Number.isInteger(pos))
- throw new TypeError();
- const len = this._value.length || 1;
- if (pos == -1)
- this._pos = len;
- else if (pos < 1 || pos > len)
- throw new RangeError();
- else
- this._pos = pos;
- this.resetWidget();
- }
- });
- Object.defineProperty(SetOfInspector.prototype, 'defaultItem', {
- get: function() {
- return this._defaultItem;
- },
- set: function(val) {
- this._defaultItem = val;
- }
- });
- SetOfInspector.prototype.validate = function(val) {
- return val === null || Array.isArray(val);
- };
- SetOfInspector.prototype.normalize = function(val) {
- if (val === null)
- val = [];
- else if (this._max !== undefined && val.length > this._max)
- val = val.slice(0, this._max);
- return val;
- };
- SetOfInspector.prototype.get = function() {
- return this._value.length > 0 ? Array.from(this._value) : null;
- };
- SetOfInspector.prototype.set = function(val) {
- if (!this.validate(val))
- return false;
- val = this.normalize(val);
- if (this._value !== val) {
- this._value = val === null ? null : Array.from(val);
- if (val === null || this._pos > val.length)
- this._pos = 1;
- this.resetWidget();
- }
- return true;
- };
- SetOfInspector.prototype.reset = function() {
- return this._inspector.reset();
- };
- SetOfInspector.prototype.disable = function() {
- this._inspector.disable();
- if (this._previousWidget)
- this._previousWidget.disabled = true;
- if (this._nextWidget)
- this._nextWidget.disabled = true;
- if (this._addWidget)
- this._addWidget.disabled = true;
- if (this._insertWidget)
- this._insertWidget.disabled = true;
- if (this._removeWidget)
- this._removeWidget.disabled = true;
- if (this._shiftWidget)
- this._shiftWidget.disabled = true;
- if (this._unshiftWidget)
- this._unshiftWidget.disabled = true;
- if (this._posWidget)
- this._posWidget.hidden = true;
- return this;
- };
- SetOfInspector.prototype.enable = function() {
- this._inspector.enable();
- if (this._previousWidget)
- this._previousWidget.disabled = this._value.length <= 1;
- if (this._nextWidget)
- this._nextWidget.disabled = this._value.length <= 1;
- if (this._addWidget)
- this._addWidget.disabled = this._max && this._value.length >= this._max;
- if (this._insertWidget)
- this._addWidget.disabled = this._max && this._value.length >= this._max;
- if (this._removeWidget)
- this._removeWidget.disabled = this._min && this._value.length <= this._min;
- if (this._shiftWidget)
- this._shiftWidget.disabled = this._value.length <= 1;
- if (this._unshiftWidget)
- this._unshiftWidget.disabled = this._value.length <= 1;
- if (this._posWidget)
- this._posWidget.hidden = false;
- return this;
- };
- SetOfInspector.prototype.inspectorValueChanged = function(sender) {
- this._value[this._pos-1] = sender.get();
- this.nextRespondTo('inspectorValueChanged', this);
- return true;
- };
- SetOfInspector.prototype.forward = function(f, ...args) {
- this._inspector.forwardTo(f, args);
- };
- SetOfInspector.prototype.addItem = function() {
- if (this._max && this._value.length >= this._max)
- return false;
- this._value.splice(this._pos, 0, this.defaultItem);
- if (this._value.length == 1)
- this._pos = 1;
- else
- this._pos++;
- this.resetWidget();
- return true;
- };
- SetOfInspector.prototype.insertItem = function() {
- if (this._max && this._value.length >= this._max)
- return false;
- this._value.splice(this._pos-1, 0, this.defaultItem);
- this.resetWidget();
- return true;
- };
- SetOfInspector.prototype.removeItem = function() {
- if (this._value.length == 0 || (this._min && this._value.length <= this._min))
- return false;
- this._value.splice(this._pos-1, 1);
- if (this._value.length == 0)
- this._pos = 1;
- else if (this._pos > this._value.length)
- this._pos = this._value.length;
- this.resetWidget();
- return true;
- };
- SetOfInspector.prototype.moveItem = function(from, to = false) {
- if (this._value.length <= 1)
- return false;
- if (to === false)
- to = from, from = this._pos;
- if (from < 1 || to < 1 || from == to || from > this._value.length || to > this._value.length)
- return false;
- const val = this._value[from-1];
- this._value[from-1] = this._value[to-1];
- this._value[to-1] = val;
- this.resetWidget();
- return true;
- };
- SetOfInspector.prototype.shiftItem = function() {
- if (this._value.length <= 1)
- return false;
- const from = this._pos;
- const to = from == 1 ? this._value.length : from-1;
- const val = this._value[from-1];
- this._value.splice(from-1, 1);
- this._value.splice(to-1, 0, val);
- this._pos = to;
- this.resetWidget();
- return true;
- };
- SetOfInspector.prototype.unshiftItem = function() {
- if (this._value.length <= 1)
- return false;
- const from = this._pos;
- const to = from == this._value.length ? 1 : from+1;
- const val = this._value[from-1];
- this._value.splice(from-1, 1);
- this._value.splice(to-1, 0, val);
- this._pos = to;
- this.resetWidget();
- return true;
- };
- SetOfInspector.prototype.setPreviousWidget = function(w) {
- w.addEventListener('click', () => {
- if (this._value.length > 0) {
- this._pos = this._pos == 1 ? this._value.length : this._pos-1;
- this.resetWidget();
- }
- });
- w.disabled = this._value.length <= 1;
- this._previousWidget = w;
- return this;
- };
- SetOfInspector.prototype.setNextWidget = function(w) {
- w.addEventListener('click', () => {
- if (this._value.length > 0) {
- this._pos = this._pos == this._value.length ? 1 : this._pos+1;
- this.resetWidget();
- }
- });
- w.disabled = this._value.length <= 1;
- this._nextWidget = w;
- return this;
- };
- SetOfInspector.prototype.setAddWidget = function(w) {
- w.addEventListener('click', () => {
- if (this.addItem())
- this.nextRespondTo('inspectorValueChanged', this);
- });
- w.disabled = this._max && this._value.length >= this._max;
- this._addWidget = w;
- return this;
- };
- SetOfInspector.prototype.setInsertWidget = function(w) {
- w.addEventListener('click', () => {
- if (this.insertItem())
- this.nextRespondTo('inspectorValueChanged', this);
- });
- w.disabled = this._max && this._value.length >= this._max;
- this._insertWidget = w;
- return this;
- };
- SetOfInspector.prototype.setRemoveWidget = function(w) {
- w.addEventListener('click', () => {
- if (this.removeItem())
- this.nextRespondTo('inspectorValueChanged', this);
- });
- w.disabled = this._min && this._value.length <= this._min;
- this._removeWidget = w;
- return this;
- };
- SetOfInspector.prototype.setShiftWidget = function(w) {
- w.addEventListener('click', () => {
- if (this.shiftItem())
- this.nextRespondTo('inspectorValueChanged', this);
- });
- w.disabled = this._value.length <= 1;
- this._shiftWidget = w;
- return this;
- };
- SetOfInspector.prototype.setUnshiftWidget = function(w) {
- w.addEventListener('click', () => {
- if (this.unshiftItem())
- this.nextRespondTo('inspectorValueChanged', this);
- });
- w.disabled = this._value.length <= 1;
- this._unshiftWidget = w;
- return this;
- };
- SetOfInspector.prototype.setIndexWidget = function(w) {
- w.innerText = this._value.length > 0 ? `${this._pos} / ${this._value.length}` : '';
- this._posWidget = w;
- return this;
- };
- SetOfInspector.prototype.resetWidget = function() {
- this._inspector.set(this._value[this._pos-1]);
- if (this._previousWidget)
- this._previousWidget.disabled = this._value.length <= 1;
- if (this._nextWidget)
- this._nextWidget.disabled = this._value.length <= 1;
- if (this._addWidget)
- this._addWidget.disabled = this._max && this._value.length >= this._max;
- if (this._insertWidget)
- this._insertWidget.disabled = this._max && this._value.length >= this._max;
- if (this._removeWidget)
- this._removeWidget.disabled = this._min && this._value.length <= this._min;
- if (this._shiftWidget)
- this._shiftWidget.disabled = this._value.length <= 1;
- if (this._unshiftWidget)
- this._unshiftWidget.disabled = this._value.length <= 1;
- if (this._posWidget)
- this._posWidget.innerText = this._value.length > 0 ? `${this._pos} / ${this._value.length}` : '';
- return this;
- };
- SetOfInspector.prototype.setWidget = function(w) {
- View.prototype.setWidget.call(this, w);
- return this;
- };
- SetOfInspector.prototype.destroyWidget = function() {
- this._inspector.destroyWidget();
- View.prototype.destroyWidget.call(this);
- return this;
- };
Test
- <?php $colors=['#541743', '#8C0C3C', '#C10037', '#F75431', '#F7BD00']; ?>
Définit les couleurs initiales du panneau de bandes des couleurs. NOTE : Les largeurs des bandes de couleurs sont initialisées aléatoirement.
- <?php $stripesmin=2; ?>
- <?php $stripesmax=6; ?>
- <?php $stripewidth=40; ?>
- <?php $stripeminwidth=10; ?>
- <?php $stripemaxwidth=60; ?>
Définit le nombre minimum et maximum de bandes, la largeur initiale d'une nouvelle bande, la largeur minimum et maximum d'une bande.
- <?php head('javascript', 'jquery.minicolors'); ?>
- <?php head('stylesheet', 'jquery.minicolors', 'screen'); ?>
Ajoute les balises <script src="/js/jquery.minicolors.js"/>
et <link rel="stylesheet" href="/css/jquery.minicolors.css" media="screen"/>
à la section <head>
du document HTML pour charger le code et la feuille de style de MiniColors en jQuery.
RAPPEL : head
est une fonction d'iZend.
Adaptez le code à votre environnement de développement.
- <?php $id=uniqid('id'); ?>
Définit l'identifiant de la <div>
qui encadre le HTML du programme de test.
- .test_display {
- display: flex;
- margin-bottom: 10px;
- border-radius: 3px;
- }
Configure la <div>
qui contient les bandes de couleurs.
- <div id="<?php echo $id; ?>" class="noprint">
- <div class="ojs">
- <div id="control_panel_1">
- <span><button type="submit" class="ojs_button tiny round"><i class="fas fa-caret-left"></i></button></span>
- <span><button type="submit" class="ojs_button tiny round"><i class="fas fa-caret-right"></i></button></span>
- <span><output></output></span>
- </div>
- <div class="test_display">
- <canvas width="360" height="120"></canvas>
- </div>
- <div id="control_panel_2">
- <span><button type="submit" class="ojs_button tiny"><i class="fas fa-sm fa-plus"></i></button></span>
- <span><button type="submit" class="ojs_button tiny"><i class="fas fa-sm fa-minus"></i></button></span>
- <span><button type="submit" class="ojs_button tiny"><i class="fas fa-sm fa-arrow-left"></i></button></span>
- <span><button type="submit" class="ojs_button tiny"><i class="fas fa-sm fa-arrow-right"></i></button></span>
- </div>
- <div>
- <span class="color_panel"></span>
- <span>
- <input id="stripe_size" type="range" min="<?php echo $stripeminwidth; ?>" max="<?php echo $stripemaxwidth; ?>" step="10" value="<?php echo $stripewidth; ?>"/>
- <output for="stripe_size"><?php echo $stripewidth; ?></output>
- </span>
- </div>
- </div>
- </div>
Crée les boutons de contrôle, le canevas pour l'affichage des bandes de couleurs et les widgets des instances de ColorPanel et RangeInspector.
- <?php head('javascript', '/objectivejs/Objective.js'); ?>
- <?php head('javascript', '/objectivejs/Validator.js'); ?>
- <?php head('javascript', '/objectivejs/Responder.js'); ?>
- <?php head('javascript', '/objectivejs/View.js'); ?>
- <?php head('javascript', '/objectivejs/Inspector.js'); ?>
- <?php head('javascript', '/objectivejs/NumberInspector.js'); ?>
- <?php head('javascript', '/objectivejs/RangeInspector.js'); ?>
- <?php head('javascript', '/objectivejs/ColorInspector.js'); ?>
- <?php head('javascript', '/objectivejs/SequenceInspector.js'); ?>
- <?php head('javascript', '/objectivejs/SetOfInspector.js'); ?>
Inclut le code de toutes les classes nécessaires.
- function Stripes() {
- View.call(this);
- this._colors = null;
- this._selectable = false;
- this._click = null;
- }
- Stripes.prototype = Object.create(View.prototype);
- Object.defineProperty(Stripes.prototype, 'constructor', { value: Stripes, enumerable: false, writable: true });
Une instance de Stripes affiche une série de bandes de couleurs dans un canevas. En option, l'utilisateur peut cliquer dans une bande de couleur pour notifier une sélection.
Une instance de Stripes hérite de la classe View.
_colors
contient le tableau des couleurs avec leurs largeurs.
_selectable
vaut true
si l'utilisateur peut cliquer dans une bande de couleur pour la sélectionner.
_click
contient la fonction de l'écouteur qui réagit à un clic dans le canevas.
- Object.defineProperty(Stripes.prototype, 'colors', {
- get: function() {
- return this._colors;
- },
- set: function(colors) {
- this._colors = colors;
- if (this.interfaced())
- this.resetWidget();
- }
- });
Définit les accesseurs get
et set
de la propriété colors
.
- Stripes.prototype.enable = function() {
- if (this._selectable)
- return this;
- if (this._click === null) {
- this._click = () => {
- if (this._colors === null)
- return;
- const rect = event.target.getBoundingClientRect();
- const x = event.clientX - rect.left;
- for (let dx = 0, i = 0; i < this._colors.length; i++) {
- dx += this._colors[i].width;
- if (dx >= x) {
- this.notify('stripeSelected', this, i+1, this._colors[i]);
- break;
- }
- }
- };
- }
- this.addEventListener('click', this._click);
- this.setStyle('cursor', 'pointer');
- this._selectable = true;
- return this;
- }
enable
programme la notification du message stripeSelected en réponse à un clic dans le canevas de this
avec en paramètres le numéro de la bande sélectionnée et la couleur correspondante avec sa largeur.
La fonction de l'écouteur de l'événement click est sauvegardée dans la propriété _click
de this
.
enable
change le type du pointeur de la souris dans le canevas de this
à pointer
.
- Stripes.prototype.disable = function() {
- if (!this._selectable)
- return this;
- this.removeEventListener('click', this._click);
- this.setStyle('cursor', 'default');
- this._selectable = false;
- return this;
- }
disable
déprogramme la notification du message stripeSelected en réponse à un clic dans le canevas de this
.
disable
change le type du pointeur de la souris dans le canevas de this
à default
.
- Stripes.prototype.resetWidget = function() {
- const canvas = this._widget;
- const ctx = canvas.getContext('2d');
- if (this._colors === null) {
- canvas.width = 0;
- return this;
- }
- let width = 0;
- for (let c of this._colors)
- width += c.width;
- canvas.width = width;
- let x = 0;
- for (let c of this._colors) {
- ctx.fillStyle = c.color;
- ctx.fillRect(x, 0, c.width, canvas.height);
- x += c.width;
- }
- return this;
- }
resetWidget
remplit le canevas de this
avec autant de rectangles qu'il y a de couleurs avec leurs largeurs dans this
.
- Stripes.prototype.setWidget = function(w) {
- if (w.tagName != 'CANVAS')
- throw new TypeError();
- View.prototype.setWidget.call(this, w);
- this.enable();
- return this;
- }
setWidget
vérifie si w
est un canevas, appelle la méthode setWidget
héritée de la classe View, active la sélection d'une bande de couleur.
- function Tester(display, inspector) {
- Responder.call(this);
- display.addListener(this);
- this._display = display;
- inspector.addNextResponder(this);
- this._inspector = inspector;
- }
- Tester.prototype = Object.create(Responder.prototype);
- Object.defineProperty(Tester.prototype, 'constructor', { value: Tester, enumerable: false, writable: true });
Une instance de Tester relie ensemble une instance de Stripes et une instance de SetOfInspector.
- Tester.prototype.inspectorValueChanged = function(sender) {
- if (sender === this._inspector)
- this._display.colors = sender.get();
- return true;
- }
Quand la valeur éditée par l'inspecteur change, la liste des couleurs de l'affichage est modifiée.
- Tester.prototype.stripeSelected = function(sender, i) {
- this._inspector.itemIndex = i;
- }
Quand une bande de couleur est sélectionnée dans l'affichage par l'utilisateur, l'élément correspondant est édité par l'inspecteur.
- Tester.prototype.enable = function() {
- this._display.enable();
- this._inspector.enable();
- return this;
- }
enable
active l'inspecteur et l'affichage.
- Tester.prototype.disable = function() {
- this._display.disable();
- this._inspector.disable();
- return this;
- }
disable
désactive l'inspecteur et l'affichage.
- const colors = [<?php echo implode(',', array_map(function($c) { return "{color: '$c', width: " . rand(2, 5)*10 . '}'; }, $colors)); ?>];
Construit la liste initiale des bandes de couleurs éditée par le programme de test avec les couleurs définies en PHP et des largeurs aléatoires.
- const container = document.querySelector('#<?php echo $id; ?>');
Retrouve le widget qui encadre l'interface du programme de test.
- const display = new Stripes();
- display.setWidget(container.querySelector('.test_display canvas'));
- display.colors = colors;
Crée l'instance de Stripes et l'initialise avec la liste de couleurs avec leurs largeurs.
- const colorInspector = new ColorInspector('<?php echo $colors[0]; ?>');
- colorInspector.createManagedWidget(container.querySelector('.color_panel'));
Crée l'instance de ColorInspector qui édite la couleur d'une bande de couleur.
- const widthInspector = new RangeInspector(<?php echo $stripewidth; ?>, { min: <?php echo $stripeminwidth; ?>, max: <?php echo $stripemaxwidth; ?> });
- widthInspector.setManagedWidget(container.querySelector('#stripe_size')).resetWidget();
Crée l'instance de RangeInspector qui édite la largeur d'une bande de couleur.
- const stripeInspector = new SequenceInspector({ color: colorInspector, width: widthInspector });
Crée l'instance de SequenceInspector qui édite la couleur et la largeur d'une bande de couleur.
- const inspector = new SetOfInspector(stripeInspector, { min: <?php echo $stripesmin; ?>, max: <?php echo $stripesmax; ?>, defaultItem: {color: ColorInspector.defaultColor, width: <?php echo $stripewidth; ?>} });
Crée l'instance de SetOfInspector qui édite la liste de couleurs avec leurs largeurs avec un nombre minimum et maximum d'éléments et une couleur avec une largeur qui servira à initialiser un nouvel élément.
- inspector.setPreviousWidget(container.querySelector('#control_panel_1 span:nth-child(1) button'));
- inspector.setNextWidget(container.querySelector('#control_panel_1 span:nth-child(2) button'));
- inspector.setAddWidget(container.querySelector('#control_panel_2 span:nth-child(1) button'));
- inspector.setRemoveWidget(container.querySelector('#control_panel_2 span:nth-child(2) button'));
- inspector.setShiftWidget(container.querySelector('#control_panel_2 span:nth-child(3) button'));
- inspector.setUnshiftWidget(container.querySelector('#control_panel_2 span:nth-child(4) button'));
- inspector.setIndexWidget(container.querySelector('#control_panel_1 output'));
Associe l'inspecteur avec tous les boutons de contrôles.
- inspector.set(colors);
Initialise l'inspecteur avec la liste des couleurs avec leurs largeurs.
- const tester = new Tester(display, inspector);
Crée l'instance de Tester qui connecte les instances de Stripes et de SetOfInspector.
VOIR AUSSI
Objective, Inspector, ColorInspector, RangeInspector, SequenceInspector
Commentaires