Releases: eclipsesource/tabris-js
v2.4.0
New RefreshComposite for "pull-to-refresh" gesture
The new widget RefreshComposite
allows to perform the "pull-to-refresh" gesture on a simple layout hierarchy. The API and features are similar to the CollectionView
but the usage is as simple as replacing a regular Composite
with a RefreshComposite
.
The following example lets the RefreshComposite
show activity for 2 seconds.
new RefreshComposite({
left: 0, right: 0, top: 0, bottom: 0,
}).on('refresh', () => setTimeout(({target}) => target.refreshIndicator = false, 2000))
.appendTo(ui.contentView);
New printing support via tabris.Printer
Support for native printing has been added via tabris.Printer
. Currently only documents in PDF format are supported.
A Printer
has a method print(..)
which takes the data to print as a ByteArray
and an options object where a jobName
can be provided. A native printing dialog will be shown where additional settings can be applied. The print(..)
method returns a promise which resolves when the print job has been dispatched or throws an error otherwise.
fetch(app.getResourceLocation('resources/example.pdf'))
.then(res => res.arrayBuffer())
.then(data => printer.print(data, {jobName: 'tabris print example'}))
.then(event => console.log('Finished', event))
.catch(err => console.error(err));
ImageView supports "pinch-to-zoom" gesture
A new "pinch-to-zoom" gesture can be used on an ImageView
to magnify an image. To enable the gesture a new property zoomEnabled
along with a zoomLevel
, minZoomLevel
and maxZoomLevel
has been added. The properties also dispatch the expected change events when the user uses a gesture to zoom.
The resting position of a zoomed image is 1.0 with the minZoomLevel
also on 1.0 and the maxZoomLevel
at 3.0.
let imageView = new ImageView({
left: 0, right: 0, top: 0, bottom: 0,
image: 'resources/salad.jpg',
zoomEnabled: true
}).on('zoomChanged', ({value: zoomLevel}) => console.log(zoomLevel)
.appendTo(ui.contentView);
Added support for "next()" in layout references
Similar to the keyword prev()
, next()
can be used in a layout declaration. It references the next widget sibling in the parent widget after the current widget.
New API to close a running app
To close a running app the following call can be made on the tabris.App
. Closing a running app is only supported on Android.
tabris.app.close();
API additions for the TextInput widget
The TextInput
gained two new properties to better integrate into the UI theme and to deal with passwords.
- The new property
cursorColor
allows to give the cursor a specific color. Only supported on iOS. - The new property
revealPassword
toggles the visibility of a password inside aTextInput
withtype
password. This addition allows to create common sign-in UIs with a checkbox "Show password".
New TabFolder navigation events
The TabFolder
has received new events for tab switching. The appear
event is fired on a Tab
when the tab becomes the selected tab. Complementary, the previously selected Tab
fires the disappear
event.
In case the currently selected Tab
is disposed, the TabFolder
now correctly fires a selectionChanged
event with the newly selected tab as the selection.
2.3.0
Custom font support
An API is provided to register a font to use throughout the app. It currently supports TrueType and OpenType fonts.
iPhone X support
Tabris.js apps are now displayed in full screen on the iPhone X and widgets have been optimized for the new hardware specifics.
More fine-grained "autoCapitalize" property of TextInput
TextInput can now be configured to auto capitalize the first word of a sentence, every word or every letter.
NavigationBar theme (Android)
The theme enables customization of the appearance of the navigation bar buttons on devices running Android version 8+.
2.2.0
New date and time input dialogs
To input date or time with native UI elements, a new DateDialog
and TimeDialog
has been added. These dialogs accept listeners which are informed when a new value is picked. To open the DateDialog
the following code can be used:
new DateDialog({
date: new Date()
}).on({
select: ({date}) => console.log(date),
}).open();
New device vendor property
To retrieve the devices vendor a new property tabris.device.vendor
has been added. It returns the device manufacturer, e.g.: "Apple" or "Samsung".
Functions as JSX elements
Functions that return a WidgetCollection can now be used as JSX elements. As per convention their name has to start with an uppercase letter. The function is called with two arguments: The element's attributes as an object and its children (if any) as an array. An example of this feature would be to call a given widget factory a given number of times:
function Repeat(properties: {times: number}, [callback]: [() => Widget]): WidgetCollection<Widget> {
let result = [];
for (let i = 0; i < properties.times; i++) {
result[i] = callback();
}
return new WidgetCollection(result);
}
It can then be used like a regular element:
ui.contentView.append(
<Repeat times={10}>{() => <textView top='prev() 10'>Hello Again!</textView>}</Repeat>
)
Note that this example assumes that the element has exactly one child (the callback
function), but the type and number of children are not checked at compile time. (The attributes are.) It would therefore be a good idea to check the type of the children at runtime.
Improved support for custom properties in TypeScript
When creating a custom subclass of an existing Tabris widget, new properties don't behave the same as the built-in ones out of the box. Getting the constructor as well as the set
and get
methods to accept the new properties has previously been rather tedious. Now, with the with the addition of the tsProperties
property and the Properties
and Partial
interfaces, this process has been greatly simplified.
The following example shows a custom UI component with full support of its new properties:
import {Composite, Partial, Properties} from 'tabris';
class MyCustomForm extends Composite {
public tsProperties: Properties<Composite> & Partial<this, 'foo' | 'bar'>;
// initializing plain properties is a must for "super" and "set" to work as a expected.
public foo: string = null;
public bar: number = null;
constructor(properties?: Properties<MyCustomForm>) {
super(properties);
}
}
Read the section "Interfaces" at EcmaScript 6, TypeScript and JSX for detailed explanations.
2.1.1
Tabris.js
Issues with console output for thrown errors have been fixed.
Tabris.js Developer App
Since 2.1.0, the Tabris.js developer app didn't run on iOS 9 anymore. This has been fixed in 2.1.1.
Android
- Fix an issue where touchEnd would not fire in some cases.
- Fix an issue causing images loaded from external resources not to be scaled according to the device pixel density.
- Fix various issues related to canvas scaling.
iOS
- Fix images not being scaled according to the device pixel density.
2.1.0
Launching external apps
The app
object has a new method launch()
that accepts a URL and asks the operating system to open this URL in an appropriate external app. Operating systems usually support a variety of URL schemes including http
, https
, mailto
, tel
, and sms
. Apps can also register for custom URL schemes. The method returns a promise to indicate success.
Example:
app.launch('http://tabrisjs.com')
.then(() => console.log('success'))
.catch(err => console.error(err));
New ActionSheet dialog allows to show unlimited dialog options
The new ActionSheet
allows to show a modal dialog with a set of actions. If many actions are configured the user can scroll the sheet to reveal all actions. The ActionSheet
compliments the existing AlertDialog
when many options with longer text need to be shown. Example:
new ActionSheet({
title: 'Actions',
actions: [
{title: 'Search'},
{title: 'Delete', style: 'destructive'},
{title: 'Cancel', style: 'cancel'},
]
}).on({select: ({index}) => console.log(`${index} selected`)})
.open();
Tint colors for CheckBox, RadioButton, and ActivityIndicator
CheckBox and RadioButtons support two new properties tintColor
and checkedTintColor
to set custom tint colors. When only tintColor
is set, it applies to both the checked and the unchecked state. Setting both properties allows for different tint color when checked.
Also the ActivityIndicator can be given a custom tintColor
now.
Improved console logging
The console
methods debug()
, log()
, info()
, warn()
, and error()
now use an improved formatting and print different types of values consistently across all platforms. Like the console in Node.js and in web browsers, they now also support printf-like placeholders. Example:
console.log('processed %d items in %d ms', items.length, time);
Extended file system API
The new file system API introduced in Tabris.js 2.0 has been extended to support reading and writing of text files and listing directories. The functions remain similar to those known from Node.js except that they return a Promise instead of accepting a callback.
To read a text file, call fs.readFile()
with the encoding in an additional argument. The returned promise will then resolve to a string instead ofreaddir
function, an array buffer:
fs.readFile(path, 'utf-8')
.then(text => console.log('read text from %s:', path, text))
.catch(err => console.error(err));
To write a text file, pass a string instead of an array buffer as second argument to fs.writeFile()
. You can also specify an encoding, if you don't, UTF-8 will be used:
fs.writeFile(path, 'Hello World!', 'ascii')
.then(() => console.log('file written:', path))
.catch(err => console.error(err));
To list the contents of a directory, use the new function fs.readDir()
that returns an array with the names of all files and sub-directories of the given directory, not including '.'
and '..'
. Note that, unlike its equivalent in Node.js, this method name is written in camel case to be consistent with the other functions.
Padding on Composite
Sometimes, it was necessary to include a wrapper composite just to add some additional white space to a layout. To simplify those layouts, we've added a padding
property to Composite that allows to add space inside the widget's bounds. The padding can either be set to an object with any of the fields left
, right
, top
, and bottom
, or to a single number which would then be applied to all four sides.
new Composite({
padding: {left: 2, right: 2, bottom: 10}
});
Font property in CanvasContext
The CanvasContext now supports the property font
, allowing you to set the text size, font family, weight and style.
Listener Registration via JSX
It's now possible register listeners directly in JSX. Any attribute that follows the naming scheme on{EventType}
is used to register a listener with that event. For example:
<button text='Show Message' onSelect={handleButtonSelect}/>
TypeScript Interfaces
The tabris module now exports the TypeScript interfaces used by the tabris API, including all parameter objects. This is useful when creating your own widgets classes in TypeScript. For details see documentation.
2.0.0
2.0.0 RC 2
Support for declarative UI using JSX
Widgets can now be created using JSX syntax. When using TypeScript this requires no extra dependencies, simply generate a new TypeScript project using tabris init
and create a file with the .tsx
file extension. Example:
import {ui} from 'tabris';
ui.contentView.append(
<tabFolder left={0} top={0} right={0} bottom={0}>
<tab title='First tab'>
<textView text='Hello World' />
</tab>
</tabFolder>
);
JSX can be much more expressive than pure JavaScript/TypeScript. Compare the "input" example in JavaScript vs. TypeScript/JSX.
New basic filesystem API
We started to implement a basic filesystem API in Tabris.js. This first version can only read, write, and delete binary files. The API will be extended over time.
The new fs
object provides two properties filesDir
and cacheDir
that point to the base paths for the app. Only files in these directories can be accessed.
The methods writeFile(path, data)
, readFile(path)
, and removeFile(path)
are all asynchronous and return a promise.
Example:
let data = new Uint8Array([1, 2, 3]).buffer;
fs.writeFile(fs.fileDir + '/test.data', data)
.then(() => console.log( data.byteLength + ' bytes written'))
.catch(err => console.error(err));
See the documentation for the details.
New data property on Widget
To attach custom data to a Widget without the risk of clashes with widget internals, you can now use the data
property. This property holds an empty object that is not used by the framework and can be freely used by the application code. Manipulations on this object will not affect the widget itself. Example:
widget.data.myItem = 23;
New load() method on CollectionView
We introduced a new method load()
in CollectionView that replaces the current items with a new ones. To load new data into a CollectionView, use this method instead of setting the itemCount
property.
Support for selectors in NavigationView.pages()
The pages()
method on NavigationView now accepts a selector argument similar to children()
to filter the list of pages. Example:
navigationView.pages('.temporary').dispose();
Introduce tintColor on Slider and ProgressBar
The color of the indicator in Slider and ProgressBar widgets can now be controlled by the new property tintColor
. This had been possible with textColor
, which is no longer supported on these widgets.
Windows support for existing (pre-RC2) Tabris.js APIs:
- Support for
tabris.app
eventsbackground
,foreground
,pause
andresume
.terminate
will not be supported in the foreseeable future. - Support for
tabris.app
propertiesappID
,version
andversionID
. - Support for
tabris.CollectionView
propertycellType
, and support for automaticitemHeight
. - Support for
tabris.CollectionView
methodreveal
- Support for
tabris.AlertDialog
. Theneutral
button is not yet supported. - Support for
WebSocket
.
Windows changes on TabFolder
- Property
background
is now applied only to the tab bar, as on Android. - Property
textColor
will not be supported in the foreseeable future. - New property
win_tabBarTheme
allows setting the windows dark/light theme on the tab bar only.
Build service uses Tabris CLI
When you build your Tabris 2.0 app with the online build service at tabrisjs.com, this build will now use the Tabris CLI. As a notable difference to 1.x, apps that built in debug mode won't be wrapped in a developer app anymore. You can load a remote app using the new URL field in the developer console. Also note that the developer console must be enabled explicitly in your config.xml
, e.g.:
<preference name="EnableDeveloperConsole" value="$IS_DEBUG" />
2.0.0 RC 1
With this first release candidate, we've finalized the API for Tabris.js 2.0.
New CollectionView API
The CollectionView has a new API based on item indexes instead of the items itself. This gives the developer more control over binding different types of models to a CollectionView.
The items
property has been replaced by a new property itemCount
that controls the number of items to be displayed. To add or remove items at runtime, the methods insert()
and remove()
can be used, however, insert()
now expects and item count instead of an array.
The cells of a CollectionView must now be created by the application in the callback createCell
, which replaces the initializeCell
callback. Any type of widget can be used as a cell. The type Cell has become obsolete and was removed. Example:
function createCell(type) {
return new TextView({
font: type === 'header' ? 'bold 18px' : '14px'
});
}
Instead of the change:item
event, the cells are now populated in a dedicated updateCell
callback that receives the cell view and the item index to show:
function updateCell(cell, index) {
cell.text = items[index].name;
}
The property itemHeight
has been renamed to cellHeight
for consistency. This property now also supports the value 'auto'
that will calculate the height of each cell individually.
New Picker API
Since the CollectionView now works on item count and indexes instead of an array of items, the Picker API now follows the same approach.
The property items
has been replaced by itemCount
. The property selection
that accepted the selected item has been removed in favor of the existing property selectionIndex
.
The itemText
callback is now required to provide a text for a given item. This callback is now called with an index instead of an item.
NavigationView
Removing hidden pages from a NavigationView using detach()
or dispose()
does no longer auto-remove pages stacked on top of the removed page. This allows you to remove underlying pages and so to change the target for back navigation.
The height of the toolbar(s) is now available as read-only properties topToolbarHeight
and bottomToolbarHeight
.
To prepare for pre-configured page transitions, the property animated
has been replaced by a property pageAnimation
that accepts the values 'default'
and 'none'
.
WebView history support
The WebView now supports navigating in the history using the new methods goBack()
and goForward()
. To check if this navigation is currently possible, you can use the read-only properties canGoBack
and canGoForward
.
Configurable enter key
The new property enterKeyType
on TextInput
allows to specify the label or icon shown on the return key of the keyboard. Supported values are 'default'
, 'done'
, 'next'
, 'send'
, 'search'
, and 'go'
.
Renamed events
All-lowercase event names and those with a colon in their name (such as pan:left
) have been renamed to camelCase (e.g. panLeft
). This affects the following events:
- Gesture events prefixed with
pan:
andswipe:
. - The variants of the
close
event onAlertDialog
have been renamed fromclose:ok
,close:cancel
andclose:neutral
tocloseOk
,closeCancel
, andcloseNeutral
. addchild
andremovechild
are nowaddChild
andremoveChild
.- Touch events (all lowercase) renamed to
touchStart
,touchEnd
,touchCancel
andtouchMove
. - Property change events (like
change:text
) are now named after the pattern<property>Changed
. For example, the eventchange:text
becomestextChanged
.
Pan events now have separate properties for translationX
and translationY
instead of translation
to match with the properties of the Widget's transform
object. The property velocity
has been replaced by individual properties velocityX
and velocityY
.
Events API
The methods on()
, off()
, and once()
now also support maps of events and listeners to allow adding and removing multiple listeners in a single statement. Example:
new CheckBox({
left: 12, top: 12
}).on({
tap: onTap,
select: onSelect
}).appendTo(parent);
This version of these methods also offer improved typing and tool support.
Besides the target
property, all events now include the additional properties type
(the event name) and timeStamp
(the event timestamp in milliseconds). The latter replaces the property time
on touch events.
As all listeners now receive an event object, the trigger()
method also expects an object as second parameter and does not delegate additional parameters to the listeners anymore.
New fetch implementation
Instead of using a polyfill, a subset of the fetch API is now implemented in Tabris.js directly. With this change, the response objects now include the method arrayBuffer()
to allow downloading binary content using fetch:
fetch(url).then(response => response.arrayBuffer()).then(buffer => ...)
As an addition to the standard, this implementation also features a timeout
option:
fetch(url, {
timeout: 5000 // request timeout in ms
}).then(...);
Support for #RRGGBBAA colors
Color properties now accept colors in the format #rrggbbaa
where the aa
part defines the alpha channel (opacity) as hex value between 00
(transparent) and ff
(opaque).
Nightly builds
For those who like to try out the latest developments in tabris, nightly builds are now available on npm. You can install the latest nightly using npm install tabris@nightly
or include a dependency like this:
"dependencies": {
"tabris": "nightly"
}
2.0.0 Beta 2
Event listener API
We've redesigned the API for event listeners. All event listeners now receive a single event object instead of separate parameters. This change requires adjustments to all existing applications, however, it will allow us to add new event properties over time without introducing any more breaking changes.
All event objects have a target
property that references the object that received the event. All change listeners (change:property
) include a property value
that contains the new value of the changed property. Other properties are specific to the event type.
For example, an event listener for a checkbox would now look like this:
checkbox.on('select', (event) => {
event.target // the checkbox that received the event
event.checked // the checked state
});
Please refer to the documentation for the properties available on the respective events. You may also want to look at the updated snippets.
Renamed properties
On all stateful button widgets, that is CheckBox
, RadioButton
, ToggleButton
, and Switch
, the property selection
has been renamed to checked
. The select
event also contains a parameter checked
that reflects the new state.
On touch events, the properties pageX
and pageY
have been renamed to absoluteX
and absoluteY
, respectively. These properties reflect the position relative to the content view (ui.contentView
).
Animations
The events animationstart
and animationend
have been removed in favour of the new Promise API. Those events were hard to consume as they could fire before the listener was even attached and they weren't specific to a certain animation. With the Promise returned by the animate()
method, it has become much easier to perform actions on completion. Example:
widget.animate({opacity: 0}, {duration: 300, easing: 'ease-out'}).then(() => widget.dispose());
Windows support
Tabris.js can now create Windows 10 Store apps for desktop, tablet and phone. Not all APIs will be supported on the launch of Tabris.js 2.0, so keep an eye on the Windows 10 support article of the Tabris.js documentation. For now Tabris Connect can build appxbundle
files suitable for sideloading, appxupload
files for store deployment will be available at launch.
Local builds with the CLI
The latest version of tabris-cli supports building apps on your local machine by calling
tabris build [android|ios|windows]
Custom widgets
The interface for custom widgets has changed. If you've developed a native custom widget, please refer to the custom widget documentation for the new API.
Migration Guide
To facilitate the migration of existing apps, we've created a migration guide.
2.0.0 Beta 1
New UI model
Tabris.js does not dictate the use of pages anymore. Widgets can be added directly to the main area of the app.
The object tabris.ui
, which represents the UI root, now has a number of children, that represent different parts of the app's user interface:
tabris.ui.statusBar
- shows the time and some system status iconstabris.ui.navigationBar
- contains the Back, Home, etc. buttons on Androidtabris.ui.contentView
- contains the app's main UItabris.ui.drawer
- can be swiped in from the left
Widgets can be appended to contentView
, and optionally to the drawer
:
new tabris.Button({
left: 16, top: 16
}).appendTo(tabris.ui.contentView);
Drawer is now a singleton
There's only a single drawer widget available as tabris.ui.drawer
. The type tabris.Drawer
cannot be instantiated anymore. The drawer is disabled by default, to use it in an application, you have to enable it:
tabris.ui.drawer.enabled = true;
New NavigationView, changes to Page
The new widget NavigationView
offers page-based navigation. By creating multiple instances of this widget, it is now possible to have multiple page stacks in an app, for example in different tabs.
Pages are now appended to and removed from a NavigationView using the standard widget methods append()
, appendTo()
, detach()
, and dispose()
, respectively. When a page is added, it becomes the topmost page on the page stack and covers any previous page. The methods open()
and close()
on Page are obsolete and have been removed.
On back navigation, the topmost page will be disposed. When a page is removed from a NavigationView, all pages on top will be disposed automatically. To prevent pages from being automatically disposed, you can set the property autoDispose
to false
. The property topLevel
has also been removed.
The header of a NavigationView can be styled using the new properties toolbarColor
, actionColor
, titleTextColor
, and actionTextColor
.
Actions and SearchActions are now also added to instances of a NavigationView.
Remove PageSelector
The helper type PageSelector
, that showed a list of pages did not fit with the new UI model that allows for multiple page stacks. It has been removed.
Widgets
New AlertDialog
The new type AlertDialog
can be used to display modal dialogs to the user. An alert dialog can contain a message text and up to three buttons with custom texts.
ScrollView scrolling
The ScrollView
properties offsetX
and offsetY
are now read-only. To scroll programmatically, use the methods scrollToX()
and scrollToY()
. You can omit the animation by adding {animate: false}
as second parameter.
TabFolder scrolling
A new property tabMode
on TabFolder
allows to enable overflow behavior on Android. It can be set to 'fixed'
(default) or 'scrollable'
. When set to 'scrollable'
, the tabs in the tab bar will overflow and can be scrolled.
A new TabFolder event scroll
is fired when paging
is enabled and a tab is scrolled. This event can be used to implement transition effects such as parallax scrolling.
TextView selectable mode
With a new property selectable
on TextView
, you can allow copying from a TextView into the clipboard. This property is currently only support on Android.
TextInput keepFocus property
A new property keepFocus
has been introduced on TextInput
. When set to true
the TextInput
will keep its focus, even when tapped outside of the widget bounds, thereby leaving the keyboard open.
New color properties on TextInput and Picker
The new properties fillColor
and borderColor
provide more fine-grained control over the look and feel of TextInput
and Picker
widgets.
Support for data URIs in image properties
Data URIs can now be used in all widget properties that accept an image.
Properties on tabris.app
The read-only properties id
, version
, and versionCode
on tabris.app
provide information on the app id and version.
HTTP
Support for binary responses in XHR
The responseType
'arraybuffer'
is now supported by the XHR implementation in Tabris.js and supports downloading binary content.
Support for certificate pinning
A new property pinnedCertificate
on tabris.app
can be used to enable certificate pinning.
The property accepts an array of pinned certificates in the form of
[
{host: <string>, hash: <string>, algorithm: <RSA2048|RSA4096|ECDSA256>},
...
]
Overall Widget API
Direct access to widget properties
You can now access all widget properties directly, without using get()
and set()
:
button.text = 'OK';
var text = button.text;
The methods get()
and set()
remain available.
Remove support for off() with zero or one argument
In Tabris 1.0, calling a widget's off()
method with zero arguments could be used to remove all listeners from a widget and off(topic)
to remove all listeners for a given event topic. As these calls could affect other modules, they were considered risky and have been removed.
New detach method to remove widgets from their parent
Tabris widgets can be created without a parent and appended to a parent later. Using appendTo()
they can be re-parented. A new method detach()
now allows to remove a widget from its current parent for later re-use.
New includes method on WidgetCollection
You can use the new method includes()
to find out whether a widget exists in a collection, e.g.
if (parent.children().includes(widget)) {
...
}
Custom widgets
The method tabris.registerWidget
that had been recommended for custom widgets has been removed. Native widgets should now extend Widget
. We recommend to use ES6 classes and transpile the code as needed.
class MyWidget extends Widget {
get foo() {}
set foo(value) {}
...
}
Android platform
Resource drawables in image objects
A new Android specific image url scheme android-drawable
allows to load bundled resource drawables, e.g. new tabris.ImageView({image: 'android-drawable://ic_icon' })
. The bundled resources have to be placed in their respective drawable folders during app build time.
Added support for splash screens
The cordova build now supports to configure a splash screen via the <splash ..>
element. To customize the theme extend one of the SplashScreen themes.
Breaking changes
- Minimum support Android version raised to API level 17 (Android 4.2)
- Base theme to extend when creating a custom theme has been renamed from
DarkActionBar
toDarkAppBar
- TabrisActivity intent extra
theme
now expects a theme resource id instead of a qualified class name