bg-atom-redom-ui
Version:
Atom plugin UI Component library using REDOM and compliant with the Atom style guide
240 lines (216 loc) • 12.6 kB
JavaScript
import { el, mount as redomMount } from 'redom';
import { Component } from './component'
import { ComponentParams } from './componentUtils'
import { Disposables } from './Disposables'
// standard Button.
// Params:
// [name:][icon-<icnName> ][label] : The first parameter is a string that contains 1 to 3 attributes of the button.
// name : variable-like name used for the button. Useful to identify which button was activated when multiple buttons share
// a single onActivatedCB callback. A container component may also use this as the property name to store this button in.
// icon-<icnName> : a name of one of the icons in the Atom Style Guide (ctrl-shift-G). The icon will appear to left of label
// label: The text displayed in the button.
// onActivatedCB(button,e) : a callback function invoked when the button is activated by click or keyboard. This object is passed
// as the first parameter and the event that caused the activation, if any, is passed in the second parameter.
// options : an object with various optional keys. See Options: section below for details
// paramNames : intended only for derived classes. a string with a space separated list of names of properties in options that
// the derviced class will process. Button will put those in this.optParams but otherwise ignore them.
// Options:
// focusOnMouseClick : if true, change the focus to the button when its clicked with the mouse. This is the default for html
// buttons but this component changes it so that it will not steal the focus with mouse clicked. The user can still give
// it the focus by keyboard navigation. Setting this option to true, returns to the default behavior where the focus will
// be left on the button after clicking. Typically toolbar buttons should not steal the focus but form buttons may want to.
// Options:
// <DOM properties and styles> : See Component
// children : array of children components passed to Component::mount. See Component::mount
export class Button {
constructor(tagIDClasses, onActivatedCB, namedParams, ...p) {
var componentParams = new ComponentParams({
tagIDClasses: '$button.btn',
paramNames: 'focusOnMouseClick'
}, tagIDClasses, onActivatedCB, namedParams, ...p);
this.disposables = new Disposables();
this.name = componentParams.name;
this.iconName = componentParams.optParams.icon;
this.onActivatedCB = componentParams.getUnnamedCB('force');
this.optParams = componentParams.optParams;
if (this.iconName)
componentParams.className += " icon "+this.iconName;
this.el = el(componentParams.makeREDOMTagString(), Object.assign({}, componentParams.props, {style:componentParams.style}));
this.setLabel(componentParams.optParams.label);
this.el.onclick = (e)=>{this.onClick(e)};
if (! componentParams.optParams["focusOnMouseClick"])
this.el.onmousedown = ()=>{this.lastFocused=document.activeElement}
// TODO: this should use the same algorithm as Component::mount
// if (optChildren)
// for (var i=0; i<optChildren.length; i++) {
// mount(this.el, optChildren[i]);
// }
}
onClick(e) {
this.lastFocused && this.lastFocused.focus(); this.lastFocused=null;
this.onActivatedCB && this.onActivatedCB(this, e);
this.onActivated(e);
}
getLabel() {return this.label}
setLabel(label) {
this.label = label || '';
if (/^<[^>]+>/.test(this.label))
this.el.innerHTML = this.label;
else
this.el.innerText = this.label
}
onActivated(e) {}
}
// ToggleButton is two-state. When activated it toggles between pressed(true) and unpressed(false). The callback is passed the
// pressed state. The presence of the .pressed class determines the current state and styling
// Params:
// [name:][icon-<icnName> ][label] : The first parameter is a string that contains 1 to 3 attributes of the button.
// name : variable-like name used for the button. Useful to identify which button was activated when multiple buttons share
// a single onActivatedCB callback. A container component may also use this as the property name to store this button in.
// icon-<icnName> : a name of one of the icons in the Atom Style Guide (ctrl-shift-G). The icon will appear to left of label
// label: The text displayed in the button.
// onActivatedCB(isPressed, this) : a callback function invoked when the button is activated by click or keyboard. The current
// pressed state and this object are passed as parameters
// options : an object with various optional keys
// Options:
// pressed : if true, the button will start in the pressed state
// focusOnMouseClick : if true, change the focus to the button when its clicked with the mouse. This is the default for html
// buttons but this component changes it so that it will not steal the focus with mouse clicked. The user can still give
// it the focus by keyboard navigation. Setting this option to true, returns to the default behavior where the focus will
// be left on the button after clicking. Typically toolbar buttons should not steal the focus but form buttons may want to.
// Options:
// <DOM properties and styles> : See Component
// children : array of children components passed to Component::mount. See Component::mount
export class ToggleButton extends Button {
constructor(nameIconLabel, onActivatedCB, ...options) {
super({paramNames: 'pressed'}, nameIconLabel, onActivatedCB, ...options);
this.setPressedState(Boolean(this.optParams["pressed"]));
}
onClick() {
this.lastFocused && this.lastFocused.focus(); this.lastFocused=null;
this.el.classList.toggle("selected");
this.onActivatedCB && this.onActivatedCB(this.isPressed(), this);
}
isPressed() {
return this.el.classList.contains("selected");
}
setPressedState(state) {
this.el.classList.toggle("selected", state);
}
}
// CommandButton is a Button that invokes a Atom command when clicked. It is constructed from the command name and handles the
// onActivatedCB itself. It gleans default values to create a tool tip
// properties
// Params:
// cmdName : the Atom command that the button will invoke. Same syntax as keymaps.
// [name:][icon-<icnName> ][label] : The second parameter is a string that contains 1 to 3 attributes of the button.
// name : variable-like name used for the button. Useful to identify which button was activated when multiple buttons share
// a single onActivatedCB callback. A container component may also use this as the property name to store this button in.
// icon-<icnName> : a name of one of the icons in the Atom Style Guide (ctrl-shift-G). The icon will appear to left of label
// label: The text displayed in the button.
// options : an object with various optional keys
// Options:
// target : target context node for the command. default is atom.workspace.getElement()
// <DOM properties and styles> : See Component
// children : array of children components passed to Component::mount. See Component::mount
export class CommandButton extends Button {
constructor(tagIDClasses, cmdName, ...options) {
super(tagIDClasses, {paramNames:'target'}, ()=>this.onClick(), ...options);
this.cmdName = cmdName;
this.cmdTarget = this.optParams["target"] || atom.workspace.getElement();
const allCommands = atom.commands.findCommands({target: this.cmdTarget});
const command = allCommands.filter((command) => command.name === cmdName)[0] || {displayName:'unknown', description:'unknown'};
if (!this.getLabel() && !this.iconName) {
this.setLabel(command.displayName);
this.toolTipTitle = command.description;
} else
this.toolTipTitle = command.displayName;
setTimeout(()=>{
this.toolTipDispose = atom.tooltips.add(this.el, {title: this.toolTipTitle, keyBindingCommand: this.cmdName}); //, keyBindingTarget: this.cmdTarget
}, 1000);
}
onClick() {
atom.commands.dispatch(this.cmdTarget, this.cmdName);
}
destroy() {
this.toolTipDispose.dispose();
}
}
// OneShotButton stays pressed when its activated and wont fire its onActivatedCB again when clicked on until it is reset. Calling
// reset makes it appear unpressed and it can then be pressed (activated) again. It is used by RadioButtonGroup which resets all
// the other buttons when any button in the group is pressed.
// Params:
// [name:][icon-<icnName> ][label] : The first parameter is a string that contains 1 to 3 attributes of the button.
// name : variable-like name used for the button. Useful to identify which button was activated when multiple buttons share
// a single onActivatedCB callback. A container component may also use this as the property name to store this button in.
// icon-<icnName> : a name of one of the icons in the Atom Style Guide (ctrl-shift-G). The icon will appear to left of label
// label: The text displayed in the button.
// onActivatedCB(isPressed, this) : a callback function invoked when the button is activated by click or keyboard. The current
// pressed state and this object are passed as parameters
// options : an object with various optional keys
// Options:
// pressed : if true, the button will start in the pressed state
// focusOnMouseClick : if true, change the focus to the button when its clicked with the mouse. This is the default for html
// buttons but this component changes it so that it will not steal the focus with mouse clicked. The user can still give
// it the focus by keyboard navigation. Setting this option to true, returns to the default behavior where the focus will
// be left on the button after clicking. Typically toolbar buttons should not steal the focus but form buttons may want to.
// Options:
// <DOM properties and styles> : See Component
// children : array of children components passed to Component::mount. See Component::mount
export class OneShotButton extends ToggleButton {
constructor(nameIconLabel, onActivatedCB, ...options) {
super(nameIconLabel, onActivatedCB, ...options);
}
reset() {
this.setPressedState(false);
}
onClick() {
this.lastFocused && this.lastFocused.focus(); this.lastFocused=null;
if (!this.isPressed()) {
this.el.classList.toggle("selected", true);
this.onActivatedCB && this.onActivatedCB(this.name || this.el.id, this);
}
}
}
// A RadioButtonGroup is a container of OneShotButton in which exactly one is pressed at a time.
// Params:
// onChangeCB(buttonName) : a callback that gets called whenever the selection changes.
// selectedButtonName : the button that is pressed initially
// buttonsData : an array describing the buttons. The button data is the information needed for the Button constructor.
// string button data: the [name:][icon-<icnName> ][label] accepted by the Button class.
// object button data: the options object accepted by the Button class additionally with a 'label' property containing
// the [name:][icon-<icnName> ][label] parameter.
// options : the properties recognized in the options param are described in the Options section
// Options:
// <DOM properties and styles> : See Component
// children : array of children components passed to Component::mount. See Component::mount
export class RadioButtonGroup extends Component {
constructor(onChangeCB, selectedButtonName, buttonsData, ...options) {
super('$div.btn-group.mutex', ...options);
this.onChangeCB = onChangeCB;
this.value = selectedButtonName;
for (var i=0; buttonsData && i<buttonsData.length; i++) {
if (typeof buttonsData[i] == 'string') {
var btn = new OneShotButton(buttonsData[i], (buttonName)=>this.onChange(buttonName));
} else {
var btn = new OneShotButton(buttonsData[i]["label"], (buttonName)=>this.onChange(buttonName), buttonsData[i], "label");
}
btn.name = (btn.name) ? btn.name : "btn"+i;
this[btn.name] = btn;
btn.setPressedState(btn.name == selectedButtonName)
redomMount(this, this[btn.name]);
this.mounted.push(btn.name);
}
}
onChange(buttonName) {
this.value = buttonName;
for (var i=0; i>this.mounted.length; i++) {
this[this.mounted[i]].setPressedState((this.mounted[i] == buttonName));
}
this.onChangeCB(buttonName);
}
setValue(buttonName) {
this.onChange(buttonName);
this.value = buttonName;
}
}