-
Notifications
You must be signed in to change notification settings - Fork 261
JEP Navbar Buttons
This Button component is the foundation of mostly all the new UI components we're going to introduce in jetpack.
- two button modes: 'Toggle' and 'Action'
- toggle mode has two visual states, on & off
- when the button is in 'toggle' mode, the api allows developers to react to this change.
- when the api is in 'action' mode, the api allows developers to assign a callback to handle the event created when a user clicks on the button.
- an arbitrary number of buttons can be visually grouped together
- following UI specs, the size of a button can be only 16x16 (default), 32x16 or 64x16.
- button has an icon and a label. However, in default Firefox settings label for button are hidden. If an icon is not provided, the generic one is used (the puzzle's piece)
- button can be disabled
- button can have a badge to display text (just few characters, tipically numbers), see Google Chrome API for example.
I will personally leave out the "grouping" feature for the first iteration, because to me it's a different kind of object – it's not just a "visual thing", but also behavioral, see the proposal below – and should be implemented separately. Plus, we do not have any UX mockup yet for it.
Same could be applied for the "badge" feature, we still missing the UX mockup, but maybe in this case it's more strictly related to the button
itself, and could be implemented in the first iteration, but maybe makes sense to leave out as well for the moment.
I'd like also to implement Image's Sprite, in order to avoid to use several images; and an 'accessKeyfeature (or 'shortcut') where we pass a combination keys that automatically trigger the button. This it will be possible in any case using
sdk/hotkeys` module and writes some code, it would be just a sugar syntax.
But those are definitely future enhancements.
The API follows the standard convention we have in jetpack for the high level APIs.
const { Button } = require("sdk/button"); // or maybe `sdk/ui`?
// Minimal "action" button, default icon, 16x16
let actionButton = Button({
id: "my-button",
label: "My Button"
});
// Minimal "toggle" button, default icon, 16x16
let toggleButton = Button({
id: "another-button",
label: "Another Button",
// `type` takes "button" (default) or "checkbox"
type: "checkbox" // it's consistent with web and XUL
});
actionButton.on("click", function() {
// do something when the button is clicked
});
toggleButton.on("change", function() {
if (this.checked) { // consistent with web and XUL
// do something when is "on"
}
}
toggleButton.on("click", function() {
// this is fired before "change" event when the state is not changed yet
})
actionButton.on("change", function() {
// never fired, it's an action button
})
// disable a button
actionButton.disabled = true; // web / XUL
// a more complex button
let drinkButton = Button({
id: "drink-button",
label: "Drink a Beer",
image: data.url("beer.png"), // only local scheme or 'data:'
type: "checkbox",
disabled: true, // `disabled` can be set in the options too, default is `false`
// `size` can takes "small" (16, default), "medium" (32), or "large" (64)
// we can also accept both number and string identifier
size: "medium",
// badge will display up to around four characters, exceeding characters will
// be cut out
badge: {
text: "+1",
color: "#5fc24f" // any CSS color syntax is valid
},
// like other jetpack API, we can set the listener directly in the object's
// options
onChange: function() {
if (this.checked) {
// `label` and `image` can be set dynamically
this.label = "Drink a Coffee";
this.image = data.url("coffee.png");
} else {
this.label = "Drink a Beer";
this.image = data.url("beer.png");
}
},
onClick: function() {
// update the badge text each click
let drinkNumber = +this.badge.text;
this.badge.text = "+" + (drinkNumber + 1);
// update the badge background color
this.badge.color = (drinkNumber > 10) ? "#fb2500" : "#5fc24f";
}
});
// it should be possible trigger the click programmatically too
drinkButton.click();
// Once `size` is set, it can't be change, it's read-only. Therefore this code
// will throw an exception
drinkButton.size = "small";
// Group buttons together:
const { Button, ButtonSet } = require("sdk/button");
let mySet = ButtonSet({
// not sure if we need a mandatory id here, it depends by the implementation
// in XUL.
id: "my-set",
// optional. Without it all buttons with type "checkbox" can checked
// at the same time, otherwise are mutually exclusive:
exclusive: true // "mutually exclusive" is too long, but I'm not sure this is good enough
});
// read-only, it will throw an exception
myGroup.exclusive = false;
let actionButton = Button({
id: "my-button",
label: "My Button",
set: mySet
});
let toggleButton = Button({
id: "another-button",
label: "Another Button",
type: "checkbox",
set: mySet
});
let drinkButton = Button({
id: "drink-button",
label: "Drink a Beer",
image: data.url("beer.png"),
type: "checkbox",
set: mySet
});
// iterate all buttons of a set
for (let button of mySet.buttons)
console.log(button.id);
// get the checked buttons of a group, of course in case of mutuallu exclusive
// it returns an array of one element.
let checkedButtons = mySet.buttons.filter(function (button) button.checked)
// ... or maybe we could have this as shortcut:
for (let button of mySet.checkedButtons)
console.log(button.id)
// disable an entire set of buttons
mySet.disabled = true;
// add listeners to an entire set of buttons
// they are emitted after the listener added to each individual button
mySet.on("change", function(button) {
// `button` is the button that fired that event
});