widgets/valueknob.js

/*
 * This file is part of AUX.
 *
 * AUX is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * AUX is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General
 * Public License along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

import { defineChildWidget } from '../child_widget.js';
import { Widget } from './widget.js';
import { Knob } from './knob.js';
import { Value } from './value.js';
import { Label } from './label.js';
import {
  applyAttribute,
  addClass,
  removeClass,
  element,
  createID,
} from '../utils/dom.js';
import { applyLegacyUsersetEventsRanged } from '../utils/legacy_userset_events.js';
import { defineRender } from '../renderer.js';

/**
 * The <code>useraction</code> event is emitted when a widget gets modified by user interaction.
 * The event is emitted for the option <code>value</code>.
 *
 * @event ValueKnob#useraction
 *
 * @param {string} name - The name of the option which was changed due to the users action
 * @param {mixed} value - The new value of the option
 */
function valueClicked() {
  const self = this.parent;
  const knob = self.knob;
  knob.scroll.set('active', false);
  knob.drag.set('active', false);
  /**
   * Is fired when the user starts editing the value manually.
   *
   * @event ValueButton#valueedit
   *
   * @param {number} value - The value of the widget.
   */
  self.emit('valueedit', this.options.value);
}
function valueDone() {
  const self = this.parent;
  const knob = self.knob;
  knob.scroll.set('active', true);
  knob.drag.set('active', true);
  /**
   * Is fired when the user finished editing the value manually.
   *
   * @event ValueButton#valueset
   *
   * @param {number} value - The value of the widget.
   */
  self.emit('valueset', this.options.value);
}
/**
 * This widget combines a {@link Knob}, a {@link Label}  and a {@link Value} whose
 * value is synchronized. It inherits all options from {@link Knob} and {@link Value}.
 *
 * @class ValueKnob
 *
 * @extends Widget
 *
 * @param {Object} [options={ }] - An object containing initial options.
 *
 * @property {String} [options.label=false] - Label of the knob. Set to `false` to hide the element from the DOM.
 * @property {Number} [options.show_value=true] - Set to `false` to hide the {@link Value}.
 * @property {Number} [options.show_knob=true] - Set to `false` to hide the {@link Knob}.
 * @property {String} [options.layout="vertical"] - Layout of the knob. Select from `horizontal`, `vertical` (default), `left` and `right`.
 */
export class ValueKnob extends Widget {
  static get _options() {
    return {
      layout: 'string',
    };
  }

  static get options() {
    return {
      layout: 'vertical',
    };
  }

  static get renderers() {
    return [
      defineRender('layout', function (layout) {
        const E = this.element;
        removeClass(
          E,
          'aux-vertical',
          'aux-horizontal',
          'aux-left',
          'aux-right'
        );
        addClass(E, 'aux-' + layout);
      }),
      defineRender(['label', 'aria_labelledby'], function (
        label,
        aria_labelledby
      ) {
        if (aria_labelledby !== void 0) return;

        const { svg } = this.knob;
        const { _input } = this.value;

        const value = label !== false ? this.label.get('id') : null;
        applyAttribute(_input, 'aria-labelledby', value);
        applyAttribute(svg, 'aria-labelledby', value);
      }),
    ];
  }

  userset(key, value) {
    if (key === 'value' && this.knob) {
      const { transformation, snap_module } = this.knob.circular.options;
      return applyLegacyUsersetEventsRanged(
        this,
        transformation,
        snap_module,
        key,
        value
      );
    } else {
      return super.userset(key, value);
    }
  }

  initialize(options) {
    if (!options.element) options.element = element('div');
    super.initialize(options);
    /**
     * @member {HTMLDivElement} ValueKnob#element - The main DIV container.
     *   Has class <code>.aux-valueknob</code>.
     */
  }

  draw(O, element) {
    addClass(element, 'aux-valueknob');

    this.knob.drag.set('classes', this.element);
    this.knob.scroll.set('classes', this.element);

    super.draw(O, element);
  }

  getRange() {
    return this.knob.getRange();
  }

  set(key, value) {
    /* this gets triggered twice, but we need it in order to make the snapping work */
    if (key === 'value' && this.knob) value = this.knob.set('value', value);

    return super.set(key, value);
  }
}
/**
 * @member {Label} ValueKnob#label - The {@link Label} widget.
 */
defineChildWidget(ValueKnob, 'label', {
  create: Label,
  option: 'label',
  toggle_class: true,
  map_options: {
    label: 'label',
  },
  static_events: {
    initialized: function () {
      if (!this.get('id')) {
        this.set('id', createID('aux-label-'));
      }
    },
  },
});
/**
 * @member {Knob} ValueKnob#knob - The {@link Knob} widget.
 */
defineChildWidget(ValueKnob, 'knob', {
  create: Knob,
  show: true,
  inherit_options: true,
  toggle_class: true,
  blacklist_options: ['label', 'show_value'],
});
/**
 * @member {Value} ValueKnob#value - The {@link Value} widget.
 */
defineChildWidget(ValueKnob, 'value', {
  create: Value,
  show: true,
  inherit_options: true,
  map_options: {
    value: 'value',
  },
  static_events: {
    valueclicked: valueClicked,
    valuedone: valueDone,
  },
  toggle_class: true,
});