/source/class/qx/ui/form/RadioGroup.js
https://github.com/fjakobs/qooxdoo · JavaScript · 524 lines · 254 code · 70 blank · 200 comment · 42 complexity · 3ea47066ddf93d5f09e31a6a0f87c7a4 MD5 · raw file
- /* ************************************************************************
- qooxdoo - the new era of web development
- http://qooxdoo.org
- Copyright:
- 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
- License:
- MIT: https://opensource.org/licenses/MIT
- See the LICENSE file in the project's top-level directory for details.
- Authors:
- * Sebastian Werner (wpbasti)
- * Andreas Ecker (ecker)
- * Christian Hagendorn (chris_schmidt)
- * Martin Wittemann (martinwittemann)
- ************************************************************************ */
- /**
- * The radio group handles a collection of items from which only one item
- * can be selected. Selection another item will deselect the previously selected
- * item.
- *
- * This class is e.g. used to create radio groups or {@link qx.ui.form.RadioButton}
- * or {@link qx.ui.toolbar.RadioButton} instances.
- *
- * We also offer a widget for the same purpose which uses this class. So if
- * you like to act with a widget instead of a pure logic coupling of the
- * widgets, take a look at the {@link qx.ui.form.RadioButtonGroup} widget.
- */
- qx.Class.define("qx.ui.form.RadioGroup", {
- extend: qx.core.Object,
- implement: [
- qx.ui.core.ISingleSelection,
- qx.ui.form.IField,
- qx.ui.form.IForm,
- qx.ui.form.IModelSelection
- ],
- include: [qx.ui.core.MSingleSelectionHandling, qx.ui.form.MModelSelection],
- /*
- *****************************************************************************
- CONSTRUCTOR
- *****************************************************************************
- */
- /**
- * @param varargs {qx.core.Object} A variable number of items, which are
- * initially added to the radio group, the first item will be selected.
- */
- construct(varargs) {
- super();
- // create item array
- this.__items = [];
- // add listener before call add!!!
- this.addListener("changeSelection", this.__onChangeSelection, this);
- if (varargs != null) {
- this.add.apply(this, arguments);
- }
- },
- /*
- *****************************************************************************
- PROPERTIES
- *****************************************************************************
- */
- properties: {
- /**
- * The property name in each of the added widgets that is grouped
- */
- groupedProperty: {
- check: "String",
- apply: "_applyGroupedProperty",
- event: "changeGroupedProperty",
- init: "value"
- },
- /**
- * The property name in each of the added widgets that is informed of the
- * RadioGroup object it is a member of
- */
- groupProperty: {
- check: "String",
- event: "changeGroupProperty",
- init: "group"
- },
- /**
- * Whether the radio group is enabled
- */
- enabled: {
- check: "Boolean",
- apply: "_applyEnabled",
- event: "changeEnabled",
- init: true
- },
- /**
- * Whether the selection should wrap around. This means that the successor of
- * the last item is the first item.
- */
- wrap: {
- check: "Boolean",
- init: true
- },
- /**
- * If is set to <code>true</code> the selection could be empty,
- * otherwise is always one <code>RadioButton</code> selected.
- */
- allowEmptySelection: {
- check: "Boolean",
- init: false,
- apply: "_applyAllowEmptySelection"
- },
- /**
- * Flag signaling if the group at all is valid. All children will have the
- * same state.
- */
- valid: {
- check: "Boolean",
- init: true,
- apply: "_applyValid",
- event: "changeValid"
- },
- /**
- * Flag signaling if the group is required.
- */
- required: {
- check: "Boolean",
- init: false,
- event: "changeRequired"
- },
- /**
- * Message which is shown in an invalid tooltip.
- */
- invalidMessage: {
- check: "String",
- init: "",
- event: "changeInvalidMessage",
- apply: "_applyInvalidMessage"
- },
- /**
- * Message which is shown in an invalid tooltip if the {@link #required} is
- * set to true.
- */
- requiredInvalidMessage: {
- check: "String",
- nullable: true,
- event: "changeInvalidMessage"
- }
- },
- /*
- *****************************************************************************
- MEMBERS
- *****************************************************************************
- */
- members: {
- /** @type {qx.ui.form.IRadioItem[]} The items of the radio group */
- __items: null,
- /*
- ---------------------------------------------------------------------------
- UTILITIES
- ---------------------------------------------------------------------------
- */
- /**
- * Get all managed items
- *
- * @return {qx.ui.form.IRadioItem[]} All managed items.
- */
- getItems() {
- return this.__items;
- },
- /*
- ---------------------------------------------------------------------------
- REGISTRY
- ---------------------------------------------------------------------------
- */
- /**
- * Add the passed items to the radio group.
- *
- * @param varargs {qx.ui.form.IRadioItem} A variable number of items to add.
- */
- add(varargs) {
- var items = this.__items;
- var item;
- var groupedProperty = this.getGroupedProperty();
- var groupedPropertyUp = qx.lang.String.firstUp(groupedProperty);
- for (var i = 0, l = arguments.length; i < l; i++) {
- item = arguments[i];
- if (items.includes(item)) {
- continue;
- }
- // Register listeners
- item.addListener(
- "change" + groupedPropertyUp,
- this._onItemChangeChecked,
- this
- );
- // Push RadioButton to array
- items.push(item);
- // Inform radio button about new group
- item.set(this.getGroupProperty(), this);
- // Need to update internal value?
- if (item.get(groupedProperty)) {
- this.setSelection([item]);
- }
- }
- // Select first item when only one is registered
- if (
- !this.isAllowEmptySelection() &&
- items.length > 0 &&
- !this.getSelection()[0]
- ) {
- this.setSelection([items[0]]);
- }
- },
- /**
- * Remove an item from the radio group.
- *
- * @param item {qx.ui.form.IRadioItem} The item to remove.
- */
- remove(item) {
- var items = this.__items;
- var groupedProperty = this.getGroupedProperty();
- var groupedPropertyUp = qx.lang.String.firstUp(groupedProperty);
- if (items.includes(item)) {
- // Remove RadioButton from array
- qx.lang.Array.remove(items, item);
- // Inform radio button about new group
- if (item.get(this.getGroupProperty()) === this) {
- item.reset(this.getGroupProperty());
- }
- // Deregister listeners
- item.removeListener(
- "change" + groupedPropertyUp,
- this._onItemChangeChecked,
- this
- );
- // if the radio was checked, set internal selection to null
- if (item.get(groupedProperty)) {
- this.resetSelection();
- }
- }
- },
- /**
- * Returns an array containing the group's items.
- *
- * @return {qx.ui.form.IRadioItem[]} The item array
- */
- getChildren() {
- return this.__items;
- },
- /*
- ---------------------------------------------------------------------------
- LISTENER FOR ITEM CHANGES
- ---------------------------------------------------------------------------
- */
- /**
- * Event listener for <code>changeValue</code> event of every managed item.
- *
- * @param e {qx.event.type.Data} Data event
- */
- _onItemChangeChecked(e) {
- var item = e.getTarget();
- var groupedProperty = this.getGroupedProperty();
- if (item.get(groupedProperty)) {
- this.setSelection([item]);
- } else if (this.getSelection()[0] == item) {
- this.resetSelection();
- }
- },
- /*
- ---------------------------------------------------------------------------
- APPLY ROUTINES
- ---------------------------------------------------------------------------
- */
- // property apply
- _applyGroupedProperty(value, old) {
- var item;
- var oldFirstUp = qx.lang.String.firstUp(old);
- var newFirstUp = qx.lang.String.firstUp(value);
- for (var i = 0; i < this.__items.length; i++) {
- item = this.__items[i];
- // remove the listener for the old change event
- item.removeListener(
- "change" + oldFirstUp,
- this._onItemChangeChecked,
- this
- );
- // add the listener for the new change event
- item.removeListener(
- "change" + newFirstUp,
- this._onItemChangeChecked,
- this
- );
- }
- },
- // property apply
- _applyInvalidMessage(value, old) {
- for (var i = 0; i < this.__items.length; i++) {
- this.__items[i].setInvalidMessage(value);
- }
- },
- // property apply
- _applyValid(value, old) {
- for (var i = 0; i < this.__items.length; i++) {
- this.__items[i].setValid(value);
- }
- },
- // property apply
- _applyEnabled(value, old) {
- var items = this.__items;
- if (value == null) {
- for (var i = 0, l = items.length; i < l; i++) {
- items[i].resetEnabled();
- }
- } else {
- for (var i = 0, l = items.length; i < l; i++) {
- items[i].setEnabled(value);
- }
- }
- },
- // property apply
- _applyAllowEmptySelection(value, old) {
- if (!value && this.isSelectionEmpty()) {
- this.resetSelection();
- }
- },
- /*
- ---------------------------------------------------------------------------
- SELECTION
- ---------------------------------------------------------------------------
- */
- /**
- * Select the item following the given item.
- */
- selectNext() {
- var item = this.getSelection()[0];
- var items = this.__items;
- var index = items.indexOf(item);
- if (index == -1) {
- return;
- }
- var i = 0;
- var length = items.length;
- // Find next enabled item
- if (this.getWrap()) {
- index = (index + 1) % length;
- } else {
- index = Math.min(index + 1, length - 1);
- }
- while (i < length && !items[index].getEnabled()) {
- index = (index + 1) % length;
- i++;
- }
- this.setSelection([items[index]]);
- },
- /**
- * Select the item previous the given item.
- */
- selectPrevious() {
- var item = this.getSelection()[0];
- var items = this.__items;
- var index = items.indexOf(item);
- if (index == -1) {
- return;
- }
- var i = 0;
- var length = items.length;
- // Find previous enabled item
- if (this.getWrap()) {
- index = (index - 1 + length) % length;
- } else {
- index = Math.max(index - 1, 0);
- }
- while (i < length && !items[index].getEnabled()) {
- index = (index - 1 + length) % length;
- i++;
- }
- this.setSelection([items[index]]);
- },
- /*
- ---------------------------------------------------------------------------
- HELPER METHODS FOR SELECTION API
- ---------------------------------------------------------------------------
- */
- /**
- * Returns the items for the selection.
- *
- * @return {qx.ui.form.IRadioItem[]} Items to select.
- */
- _getItems() {
- return this.getItems();
- },
- /**
- * Returns if the selection could be empty or not.
- *
- * @return {Boolean} <code>true</code> If selection could be empty,
- * <code>false</code> otherwise.
- */
- _isAllowEmptySelection() {
- return this.isAllowEmptySelection();
- },
- /**
- * Returns whether the item is selectable. In opposite to the default
- * implementation (which checks for visible items) every radio button
- * which is part of the group is selected even if it is currently not visible.
- *
- * @param item {qx.ui.form.IRadioItem} The item to check if its selectable.
- * @return {Boolean} <code>true</code> if the item is part of the radio group
- * <code>false</code> otherwise.
- */
- _isItemSelectable(item) {
- return this.__items.indexOf(item) != -1;
- },
- /**
- * Event handler for <code>changeSelection</code>.
- *
- * @param e {qx.event.type.Data} Data event.
- */
- __onChangeSelection(e) {
- var value = e.getData()[0];
- var old = e.getOldData()[0];
- var groupedProperty = this.getGroupedProperty();
- if (old) {
- old.set(groupedProperty, false);
- }
- if (value) {
- value.set(groupedProperty, true);
- // If Group is focused, the selection was changed by keyboard. Switch focus to new value
- if (this.__isGroupFocused() && value.isFocusable()) {
- value.focus();
- }
- }
- },
- /**
- * Checks if this group is focused by checking focused state of each item
- * @returns {Boolean} result
- */
- __isGroupFocused() {
- const focusHandler = qx.ui.core.FocusHandler.getInstance();
- for (const item of this._getItems()) {
- if (focusHandler.isFocused(item)) {
- return true;
- }
- }
- return false;
- }
- },
- /*
- *****************************************************************************
- DESTRUCTOR
- *****************************************************************************
- */
- destruct() {
- this._disposeArray("__items");
- }
- });