From cf21ef60e0f7479d52af273b005afeba9f3c768d Mon Sep 17 00:00:00 2001 From: DanyLE Date: Thu, 2 Feb 2023 21:06:10 +0100 Subject: [PATCH] Update AntOSDK and SQLiteDB - AntOSDK: add some public API on grid view - SQLiteDB: add main application as a basic SQLiteBrowser --- SQLiteDB/LibSQLite.ts | 315 +++---------- SQLiteDB/build/debug/libsqlite.js | 264 +++-------- SQLiteDB/build/debug/main.js | 517 ++++++++++------------ SQLiteDB/build/debug/scheme.html | 16 +- SQLiteDB/main.ts | 292 ++++++++---- SQLiteDB/scheme.html | 16 +- libantosdk/README.md | 1 + libantosdk/build/debug/README.md | 1 + libantosdk/build/debug/core/ts/antos.d.ts | 48 +- libantosdk/build/debug/package.json | 2 +- libantosdk/build/release/libantosdk.zip | Bin 1279641 -> 1279860 bytes libantosdk/core/ts/antos.d.ts | 48 +- libantosdk/package.json | 2 +- packages.json | 2 +- 14 files changed, 676 insertions(+), 848 deletions(-) diff --git a/SQLiteDB/LibSQLite.ts b/SQLiteDB/LibSQLite.ts index 1c8a7f6..d7b4c04 100644 --- a/SQLiteDB/LibSQLite.ts +++ b/SQLiteDB/LibSQLite.ts @@ -1,259 +1,6 @@ namespace OS { export namespace API { - /** - * Generate SQL expression from input object - * - * Example of input object - * ```ts - * { - * where: { - * id$gte: 10, - * user: "dany'", - * $or: { - * 'user.email': "test@mail.com", - * age$lte: 30, - * $and: { - * 'user.birth$ne': 1986, - * age$not_between: [20,30], - * name$not_like: "%LE" - * } - * } - * }, - * fields: ['name as n', 'id', 'email'], - * order: ['user.name$asc', "id$desc"], - * joins: { - * cid: 'Category.id', - * did: 'Country.id' - * } - *} - * ``` - * This will generate the followings expressions: - * - `( self.name as n,self.id,self.email )` for fields - * - condition: - * ``` - * ( - * ( self.id >= 10 ) AND - * ( self.user = 'dany''' ) AND - * ( - * ( user.email = 'test@mail.com' ) OR - * ( self.age <= 30 ) OR - * ( - * ( user.birth != 1986 ) AND - * ( self.age NOT BETWEEN 20 AND 30 ) AND - * ( self.name NOT LIKE '%LE' ) - * ) - * ) - * ) - * ``` - * - order: `user.name ASC,self.id DESC` - * - joining: - * ``` - * INNER JOIN Category ON self.cid = Category.id - * INNER JOIN Country ON self.did = Country.id - * ``` - * - */ - class SQLiteQueryGenerator { - private _where: string; - private _fields: string; - private _order: string; - private _joins: string; - private _is_joining: boolean; - constructor(obj: GenericObject) - { - this._where = undefined; - this._fields = undefined; - this._order = undefined; - this._joins = undefined; - this._is_joining = false; - if(obj.joins) - { - this._is_joining = true; - this._joins = this.joins(obj.joins); - } - - if(obj.where) - { - this._where = this.where("$and", obj.where); - } - if(obj.fields) - { - this._fields = `( ${obj.fields.map(v=>this.infer_field(v)).join(",")} )`; - } - if(obj.order) - { - this._order = this.order_by(obj.order); - } - } - - private infer_field(k: string) : string - { - if(!this._is_joining || k.indexOf(".") > 0) return k; - return `self.${k}`; - } - - private joins(data: GenericObject): string - { - let joins_arr = []; - for(let k in data) - { - let v = data[k]; - let arr = v.split('.') - if(arr.length != 2) - { - throw new Error(__("Other table name parsing error: {0}", v).__()); - } - joins_arr.push(`INNER JOIN ${arr[0]} ON ${this.infer_field(k)} = ${v}`); - } - return joins_arr.join(" "); - } - - print() - { - console.log(this._fields); - console.log(this._where); - console.log(this._order); - console.log(this._joins); - } - - private order_by(order: string[]): string - { - if(! Array.isArray(order)) - { - throw new Error(__("Invalid type: expect array get {0}", typeof(order)).__()); - - } - return order.map((v,_) => { - const arr = v.split('$'); - if(arr.length != 2) - { - throw new Error(__("Invalid field order format {0}", v).__()); - } - switch(arr[1]) - { - case 'asc': return `${this.infer_field(arr[0])} ASC`; - case 'desc': return `${this.infer_field(arr[0])} DESC`; - default: throw new Error(__("Invalid field order type {0}", v).__()); - } - }).join(","); - } - - private escape_string(s: string) - { - let regex = /[']/g; - var chunkIndex = regex.lastIndex = 0; - var escapedVal = ''; - var match; - - while ((match = regex.exec(s))) { - escapedVal += s.slice(chunkIndex, match.index) + {'\'': '\'\''}[match[0]]; - chunkIndex = regex.lastIndex; - } - - if (chunkIndex === 0) { - // Nothing was escaped - return "'" + s + "'"; - } - - if (chunkIndex < s.length) { - return "'" + escapedVal + s.slice(chunkIndex) + "'"; - } - - return "'" + escapedVal + "'"; - } - - private parse_value(v:any, t: string[]): string - { - if(! (t as any).includes(typeof(v))) - { - throw new Error(__("Invalid type: expect [{0}] get {1}", t.join(","), typeof(v)).__()); - } - switch(typeof(v)) - { - case 'number': return JSON.stringify(v); - case 'string': return this.escape_string(v); - default: throw new Error(__("Un supported value {0} of type {1}", v, typeof(v)).__()); - } - } - - private binary(k: string,v :any) - { - const arr = k.split("$"); - if(arr.length > 2) - { - throw new Error(__("Invalid left hand side format: {0}", k).__()); - } - if(arr.length == 2) - { - switch(arr[1]) - { - case "gt": - return `( ${this.infer_field(arr[0])} > ${this.parse_value(v, ['number'])} )`; - case "gte": - return `( ${this.infer_field(arr[0])} >= ${this.parse_value(v, ['number'])} )`; - case "lt": - return `( ${this.infer_field(arr[0])} < ${this.parse_value(v, ['number'])} )`; - case "lte": - return `( ${this.infer_field(arr[0])} <= ${this.parse_value(v, ['number'])} )`; - case "ne": - return `( ${this.infer_field(arr[0])} != ${this.parse_value(v, ['number', 'string'])} )`; - case "between": - return `( ${this.infer_field(arr[0])} BETWEEN ${this.parse_value(v[0],['number'])} AND ${this.parse_value(v[1],['number'])} )`; - case "not_between": - return `( ${this.infer_field(arr[0])} NOT BETWEEN ${this.parse_value(v[0],['number'])} AND ${this.parse_value(v[1],['number'])} )`; - case "in": - return `( ${this.infer_field(arr[0])} IN [${this.parse_value(v[0],['number'])}, ${this.parse_value(v[1],['number'])}] )`; - case "not_in": - return `( ${this.infer_field(arr[0])} NOT IN [${this.parse_value(v[0],['number'])}, ${this.parse_value(v[1],['number'])}] )`; - case "like": - return `( ${this.infer_field(arr[0])} LIKE ${this.parse_value(v,['string'])} )`; - case "not_like": - return `( ${this.infer_field(arr[0])} NOT LIKE ${this.parse_value(v,['string'])} )`; - default: throw new Error(__("Unsupported operator `{0}`", arr[1]).__()); - } - } - else - { - return `( ${this.infer_field(arr[0])} = ${this.parse_value(v, ['number', 'string'])} )`; - } - - } - - private where(op:string, obj: GenericObject): string - { - let join_op = undefined; - switch(op) - { - case "$and": - join_op = " AND "; - break; - case "$or": - join_op = " OR "; - break; - default: - throw new Error(__("Invalid operator {0}", op).__()); - } - - if(typeof obj !== "object") - { - throw new Error(__("Invalid input data for operator {0}", op).__()); - } - - let arr = []; - for(let k in obj){ - if(k == "$and" || k=="$or") - { - arr.push(this.where(k, obj[k])); - } - else - { - arr.push(this.binary(k, obj[k])); - } - } - return `( ${arr.join(join_op)} )`; - } - } class SQLiteDBCore { static REGISTY: GenericObject; @@ -394,7 +141,7 @@ namespace OS { return this.request(rq); } - create_table(table, scheme): Promise + create_table(table: string, scheme: GenericObject): Promise { let rq = { action: 'create_table', @@ -464,10 +211,9 @@ namespace OS { * - other operations are not supported * * `sqlite://remote/path/to/file.db@table_name` refers to the table `table_name` in the database * - meta operation will return fileinfo with table scheme information - * - read operation will read all records by filter defined by the filter operation + * - read operation will read all records by filter defined by the filter as parameters * - write operations will insert a new record - * - rm operation will delete records by filter defined by the filter operation - * - filter operation sets the filter for the table + * - rm operation will delete records by filter as parameters * - other operations are not supported * - `sqlite://remote/path/to/file.db@table_name@id` refers to a records in `table_name` with ID `id` * - read operation will read the current record @@ -475,12 +221,57 @@ namespace OS { * - rm operation will delete current record * - other operations are not supported * - * Some example of filters: + * Example of filter: * ```ts - * handle.filter = (filter) => { - * filter.fields() - * } - * ``` + * { + * table_name:'contacts'; + * where: { + * id$gte: 10, + * user: "dany'", + * $or: { + * 'user.email': "test@mail.com", + * age$lte: 30, + * $and: { + * 'user.birth$ne': 1986, + * age$not_between: [20,30], + * name$not_like: "%LE" + * } + * } + * }, + * fields: ['name as n', 'id', 'email'], + * order: ['user.name$asc', "id$desc"], + * joins: { + * cid: 'Category.id', + * did: 'Country.id', + * uid: "User.id" + * } + *} + * ``` + * This will generate the followings expressions: + * - `( self.name as n,self.id,self.email )` for fields + * - condition: + * ``` + * ( + * ( contacts.id >= 10 ) AND + * ( contacts.user = 'dany''' ) AND + * ( + * ( user.email = 'test@mail.com' ) OR + * ( contacts.age <= 30 ) OR + * ( + * ( user.birth != 1986 ) AND + * ( contacts.age NOT BETWEEN 20 AND 30 ) AND + * ( contacts.name NOT LIKE '%LE' ) + * ) + * ) + * ) + * ``` + * - order: `user.name ASC,contacts.id DESC` + * - joining: + * ``` + * INNER JOIN Category ON contacts.cid = Category.id + * INNER JOIN Country ON contacts.did = Country.id + * INNER JOIN Country ON contacts.did = Country.id + * ``` * * @class SqliteFileHandle * @extends {BaseFileHandle} diff --git a/SQLiteDB/build/debug/libsqlite.js b/SQLiteDB/build/debug/libsqlite.js index 7095df6..4a7497d 100644 --- a/SQLiteDB/build/debug/libsqlite.js +++ b/SQLiteDB/build/debug/libsqlite.js @@ -3,210 +3,6 @@ var OS; (function (OS) { let API; (function (API) { - /** - * Generate SQL expression from input object - * - * Example of input object - * ```ts - * { - * where: { - * id$gte: 10, - * user: "dany'", - * $or: { - * 'user.email': "test@mail.com", - * age$lte: 30, - * $and: { - * 'user.birth$ne': 1986, - * age$not_between: [20,30], - * name$not_like: "%LE" - * } - * } - * }, - * fields: ['name as n', 'id', 'email'], - * order: ['user.name$asc', "id$desc"], - * joins: { - * cid: 'Category.id', - * did: 'Country.id' - * } - *} - * ``` - * This will generate the followings expressions: - * - `( self.name as n,self.id,self.email )` for fields - * - condition: - * ``` - * ( - * ( self.id >= 10 ) AND - * ( self.user = 'dany''' ) AND - * ( - * ( user.email = 'test@mail.com' ) OR - * ( self.age <= 30 ) OR - * ( - * ( user.birth != 1986 ) AND - * ( self.age NOT BETWEEN 20 AND 30 ) AND - * ( self.name NOT LIKE '%LE' ) - * ) - * ) - * ) - * ``` - * - order: `user.name ASC,self.id DESC` - * - joining: - * ``` - * INNER JOIN Category ON self.cid = Category.id - * INNER JOIN Country ON self.did = Country.id - * ``` - * - */ - class SQLiteQueryGenerator { - constructor(obj) { - this._where = undefined; - this._fields = undefined; - this._order = undefined; - this._joins = undefined; - this._is_joining = false; - if (obj.joins) { - this._is_joining = true; - this._joins = this.joins(obj.joins); - } - if (obj.where) { - this._where = this.where("$and", obj.where); - } - if (obj.fields) { - this._fields = `( ${obj.fields.map(v => this.infer_field(v)).join(",")} )`; - } - if (obj.order) { - this._order = this.order_by(obj.order); - } - } - infer_field(k) { - if (!this._is_joining || k.indexOf(".") > 0) - return k; - return `self.${k}`; - } - joins(data) { - let joins_arr = []; - for (let k in data) { - let v = data[k]; - let arr = v.split('.'); - if (arr.length != 2) { - throw new Error(__("Other table name parsing error: {0}", v).__()); - } - joins_arr.push(`INNER JOIN ${arr[0]} ON ${this.infer_field(k)} = ${v}`); - } - return joins_arr.join(" "); - } - print() { - console.log(this._fields); - console.log(this._where); - console.log(this._order); - console.log(this._joins); - } - order_by(order) { - if (!Array.isArray(order)) { - throw new Error(__("Invalid type: expect array get {0}", typeof (order)).__()); - } - return order.map((v, _) => { - const arr = v.split('$'); - if (arr.length != 2) { - throw new Error(__("Invalid field order format {0}", v).__()); - } - switch (arr[1]) { - case 'asc': return `${this.infer_field(arr[0])} ASC`; - case 'desc': return `${this.infer_field(arr[0])} DESC`; - default: throw new Error(__("Invalid field order type {0}", v).__()); - } - }).join(","); - } - escape_string(s) { - let regex = /[']/g; - var chunkIndex = regex.lastIndex = 0; - var escapedVal = ''; - var match; - while ((match = regex.exec(s))) { - escapedVal += s.slice(chunkIndex, match.index) + { '\'': '\'\'' }[match[0]]; - chunkIndex = regex.lastIndex; - } - if (chunkIndex === 0) { - // Nothing was escaped - return "'" + s + "'"; - } - if (chunkIndex < s.length) { - return "'" + escapedVal + s.slice(chunkIndex) + "'"; - } - return "'" + escapedVal + "'"; - } - parse_value(v, t) { - if (!t.includes(typeof (v))) { - throw new Error(__("Invalid type: expect [{0}] get {1}", t.join(","), typeof (v)).__()); - } - switch (typeof (v)) { - case 'number': return JSON.stringify(v); - case 'string': return this.escape_string(v); - default: throw new Error(__("Un supported value {0} of type {1}", v, typeof (v)).__()); - } - } - binary(k, v) { - const arr = k.split("$"); - if (arr.length > 2) { - throw new Error(__("Invalid left hand side format: {0}", k).__()); - } - if (arr.length == 2) { - switch (arr[1]) { - case "gt": - return `( ${this.infer_field(arr[0])} > ${this.parse_value(v, ['number'])} )`; - case "gte": - return `( ${this.infer_field(arr[0])} >= ${this.parse_value(v, ['number'])} )`; - case "lt": - return `( ${this.infer_field(arr[0])} < ${this.parse_value(v, ['number'])} )`; - case "lte": - return `( ${this.infer_field(arr[0])} <= ${this.parse_value(v, ['number'])} )`; - case "ne": - return `( ${this.infer_field(arr[0])} != ${this.parse_value(v, ['number', 'string'])} )`; - case "between": - return `( ${this.infer_field(arr[0])} BETWEEN ${this.parse_value(v[0], ['number'])} AND ${this.parse_value(v[1], ['number'])} )`; - case "not_between": - return `( ${this.infer_field(arr[0])} NOT BETWEEN ${this.parse_value(v[0], ['number'])} AND ${this.parse_value(v[1], ['number'])} )`; - case "in": - return `( ${this.infer_field(arr[0])} IN [${this.parse_value(v[0], ['number'])}, ${this.parse_value(v[1], ['number'])}] )`; - case "not_in": - return `( ${this.infer_field(arr[0])} NOT IN [${this.parse_value(v[0], ['number'])}, ${this.parse_value(v[1], ['number'])}] )`; - case "like": - return `( ${this.infer_field(arr[0])} LIKE ${this.parse_value(v, ['string'])} )`; - case "not_like": - return `( ${this.infer_field(arr[0])} NOT LIKE ${this.parse_value(v, ['string'])} )`; - default: throw new Error(__("Unsupported operator `{0}`", arr[1]).__()); - } - } - else { - return `( ${this.infer_field(arr[0])} = ${this.parse_value(v, ['number', 'string'])} )`; - } - } - where(op, obj) { - let join_op = undefined; - switch (op) { - case "$and": - join_op = " AND "; - break; - case "$or": - join_op = " OR "; - break; - default: - throw new Error(__("Invalid operator {0}", op).__()); - } - if (typeof obj !== "object") { - throw new Error(__("Invalid input data for operator {0}", op).__()); - } - let arr = []; - for (let k in obj) { - if (k == "$and" || k == "$or") { - arr.push(this.where(k, obj[k])); - } - else { - arr.push(this.binary(k, obj[k])); - } - } - return `( ${arr.join(join_op)} )`; - } - } class SQLiteDBCore { constructor(path) { if (!SQLiteDBCore.REGISTY) { @@ -376,10 +172,9 @@ var OS; * - other operations are not supported * * `sqlite://remote/path/to/file.db@table_name` refers to the table `table_name` in the database * - meta operation will return fileinfo with table scheme information - * - read operation will read all records by filter defined by the filter operation + * - read operation will read all records by filter defined by the filter as parameters * - write operations will insert a new record - * - rm operation will delete records by filter defined by the filter operation - * - filter operation sets the filter for the table + * - rm operation will delete records by filter as parameters * - other operations are not supported * - `sqlite://remote/path/to/file.db@table_name@id` refers to a records in `table_name` with ID `id` * - read operation will read the current record @@ -387,12 +182,57 @@ var OS; * - rm operation will delete current record * - other operations are not supported * - * Some example of filters: + * Example of filter: * ```ts - * handle.filter = (filter) => { - * filter.fields() - * } - * ``` + * { + * table_name:'contacts'; + * where: { + * id$gte: 10, + * user: "dany'", + * $or: { + * 'user.email': "test@mail.com", + * age$lte: 30, + * $and: { + * 'user.birth$ne': 1986, + * age$not_between: [20,30], + * name$not_like: "%LE" + * } + * } + * }, + * fields: ['name as n', 'id', 'email'], + * order: ['user.name$asc', "id$desc"], + * joins: { + * cid: 'Category.id', + * did: 'Country.id', + * uid: "User.id" + * } + *} + * ``` + * This will generate the followings expressions: + * - `( self.name as n,self.id,self.email )` for fields + * - condition: + * ``` + * ( + * ( contacts.id >= 10 ) AND + * ( contacts.user = 'dany''' ) AND + * ( + * ( user.email = 'test@mail.com' ) OR + * ( contacts.age <= 30 ) OR + * ( + * ( user.birth != 1986 ) AND + * ( contacts.age NOT BETWEEN 20 AND 30 ) AND + * ( contacts.name NOT LIKE '%LE' ) + * ) + * ) + * ) + * ``` + * - order: `user.name ASC,contacts.id DESC` + * - joining: + * ``` + * INNER JOIN Category ON contacts.cid = Category.id + * INNER JOIN Country ON contacts.did = Country.id + * INNER JOIN Country ON contacts.did = Country.id + * ``` * * @class SqliteFileHandle * @extends {BaseFileHandle} diff --git a/SQLiteDB/build/debug/main.js b/SQLiteDB/build/debug/main.js index 317e6cf..f66a559 100644 --- a/SQLiteDB/build/debug/main.js +++ b/SQLiteDB/build/debug/main.js @@ -57,47 +57,155 @@ var OS; } }); } - openFile() { - return this.openDialog("FileDialog", { - title: __("Open file"), - mimes: this.meta().mimes - }).then(async (d) => { - this.filehandle = `sqlite://${d.file.path.asFileHandle().genealogy.join("/")}`.asFileHandle(); + async openFile() { + try { + const d_1 = await this.openDialog("FileDialog", { + title: __("Open file"), + mimes: this.meta().mimes + }); + this.filehandle = `sqlite://${d_1.file.path.asFileHandle().genealogy.join("/")}`.asFileHandle(); await this.filehandle.onready(); this.list_tables(); - }) - .catch((e) => { + } + catch (e) { this.error(__("Unable to open database file: {0}", e.toString()), e); - }); + } ; } - newFile() { - return this.openDialog("FileDialog", { - title: __("Save as"), - file: "Untitled.db" - }).then(async (f) => { - var d; - d = f.file.path.asFileHandle(); + async newFile() { + try { + const f = await this.openDialog("FileDialog", { + title: __("Save as"), + file: "Untitled.db" + }); + var d_1 = f.file.path.asFileHandle(); if (f.file.type === "file") { - d = d.parent(); + d_1 = d_1.parent(); } - const target = `${d.path}/${f.name}`.asFileHandle(); + const target = `${d_1.path}/${f.name}`.asFileHandle(); this.filehandle = `sqlite://${target.genealogy.join("/")}`.asFileHandle(); await this.filehandle.onready(); this.list_tables(); - }) - .catch((e) => { + } + catch (e) { this.error(__("Unable to init database file: {0}", e.toString()), e); - }); + } } main() { this.filehandle = undefined; this.tbl_list = this.find("tbl-list"); + this.grid_table = this.find("tb-browser"); + this.grid_scheme = this.find("sch-browser"); + this.grid_table.resizable = true; + this.grid_scheme.resizable = true; + this.grid_scheme.header = [ + { text: __("Field name") }, + { text: __("Field type") }, + ]; + this.btn_loadmore = this.find("bt-load-next"); + this.container = this.find("container"); + this.bindKey("ALT-N", () => { + return this.newFile(); + }); + this.bindKey("ALT-O", () => { + return this.openFile(); + }); + this.container.ontabselect = (e) => { + if (this.container.selectedIndex == 0) { + if (!this.tbl_list.selectedItem) + return; + const scheme = this.tbl_list.selectedItem.data.handle.info.scheme; + if (!scheme) + return; + const data = []; + for (let k in scheme) { + data.push([ + { text: k }, + { text: scheme[k] } + ]); + } + this.grid_scheme.rows = data; + } + }; this.find("bt-add-table").onbtclick = (e) => { + if (!this.filehandle) { + return this.notify(__("Please open a database file")); + } this.openDialog(new NewTableDialog(), { title: __("Create new table") + }) + .then((data) => { + console.log(data); }); }; + this.btn_loadmore.onbtclick = async (e) => { + try { + await this.load_table(); + } + catch (e) { + this.error(__("Error reading table: {0}", e.toString()), e); + } + }; + this.tbl_list.onlistselect = async (_) => { + try { + if (!this.tbl_list.selectedItem) + return; + const handle = this.tbl_list.selectedItem.data.handle; + await handle.onready(); + this.last_max_id = 0; + this.grid_table.rows = []; + const headers = Object.getOwnPropertyNames(handle.info.scheme).map((e) => { + return { text: e }; + }); + this.grid_table.header = headers; + const records = await handle.read({ fields: ["COUNT(*)"] }); + this.n_records = records[0]["(COUNT(*))"]; + await this.load_table(); + this.container.selectedIndex = 1; + } + catch (e) { + this.error(__("Error reading table: {0}", e.toString()), e); + } + }; + this.openFile(); + } + async load_table() { + if (this.grid_table.rows.length >= this.n_records) { + return; + } + if (!this.tbl_list.selectedItem) + return; + const handle = this.tbl_list.selectedItem.data.handle; + await handle.onready(); + const headers = Object.getOwnPropertyNames(handle.info.scheme).map((e) => { + return { text: e }; + }); + // read all records + const records = await handle.read({ + where: { id$gt: this.last_max_id }, + limit: 10 + }); + if (records && records.length > 0) { + for (let e of records) { + const row = []; + if (e.id && e.id > this.last_max_id) { + this.last_max_id = e.id; + } + for (let v in headers) { + let text = e[headers[v].text]; + if (text.length > 100) { + text = text.substring(0, 100); + } + row.push({ + text: text, + record: e + }); + } + this.grid_table.push(row, false); + } + this.grid_table.scroll_to_bottom(); + } + this.btn_loadmore.text = `${this.grid_table.rows.length}/${this.n_records}`; } } application.SQLiteDB = SQLiteDB; @@ -109,9 +217,6 @@ var OS; constructor() { super("NewTableDialog"); } - init() { - console.log(this.constructor.scheme); - } /** * Main entry point * @@ -121,22 +226,31 @@ var OS; super.main(); this.container = this.find("container"); this.find("btnCancel").onbtclick = (e) => this.quit(); - this.find("btnAdd").onbtclick = (e) => this.addField("", "", true); + this.find("btnAdd").onbtclick = (e) => this.addField(); $(this.find("wrapper")); $(this.container) .css("overflow-y", "auto"); + this.addField(); this.find("btnOk").onbtclick = (e) => { - const inputs = $("input", this.scheme); + const input = this.find("txt-tblname"); + if (!input.value || input.value == "") { + return this.notify(__("Please enter table name")); + } + const inputs = $("input", this.container); + const lists = $("afx-list-view", this.container); + if (inputs.length == 0) { + return this.notify(__("Please define table fields")); + } let cdata = {}; - for (let i = 0; i < inputs.length; i += 2) { + for (let i = 0; i < inputs.length; i++) { const key = inputs[i].value.trim(); if (key === "") { - return this.notify(__("Key cannot be empty")); + return this.notify(__("Field name cannot be empty")); } if (cdata[key]) { - return this.notify(__("Duplicate key: {0}", key)); + return this.notify(__("Duplicate field: {0}", key)); } - cdata[key] = inputs[i + 1].value.trim(); + cdata[key] = lists[i].selectedItem.data.text; } if (this.handle) this.handle(cdata); @@ -149,64 +263,55 @@ var OS; * @private * @memberof NewTableDialog */ - addField(key, value, removable) { + addField() { const div = $("
") - .css("width", "100%") .css("display", "flex") .css("flex-direction", "row") .appendTo(this.container); $("") .attr("type", "text") - .css("width", "50%") - .css("height", "25px") - .val(key) + .css("flex", "1") .appendTo(div); - $("") - .css("width", "50%") - .css("height", "25px") + let list = $("") + .css("flex", "1") + .appendTo(div)[0]; + list.uify(this.observable); + list.dropdown = true; + list.data = [ + { text: "TEXT" }, + { text: "INTEGER" }, + { text: "REAL" }, + { text: "NUMERIC" }, + ]; + list.selected = 0; + const btn = $(""); + btn[0].uify(undefined); + btn[0].iconclass = "fa fa-minus"; + btn + .on("click", () => { + div.remove(); + }) .appendTo(div); - if (removable) { - const btn = $(""); - btn[0].uify(undefined); - $("button", btn) - .css("width", "25px") - .css("height", "25px"); - btn[0].iconclass = "fa fa-minus"; - btn - .on("click", () => { - div.remove(); - }) - .appendTo(div); - } - else { - $("
") - .css("width", "25px") - .appendTo(div); - } } } /** * Scheme definition */ NewTableDialog.scheme = `\ - - -
- -
- -
- - -
- - -
-
-
-
-
-
`; + + + + +
+ + +
+ + +
+
+
+
`; })(application = OS.application || (OS.application = {})); })(OS || (OS = {})); @@ -215,210 +320,6 @@ var OS; (function (OS) { let API; (function (API) { - /** - * Generate SQL expression from input object - * - * Example of input object - * ```ts - * { - * where: { - * id$gte: 10, - * user: "dany'", - * $or: { - * 'user.email': "test@mail.com", - * age$lte: 30, - * $and: { - * 'user.birth$ne': 1986, - * age$not_between: [20,30], - * name$not_like: "%LE" - * } - * } - * }, - * fields: ['name as n', 'id', 'email'], - * order: ['user.name$asc', "id$desc"], - * joins: { - * cid: 'Category.id', - * did: 'Country.id' - * } - *} - * ``` - * This will generate the followings expressions: - * - `( self.name as n,self.id,self.email )` for fields - * - condition: - * ``` - * ( - * ( self.id >= 10 ) AND - * ( self.user = 'dany''' ) AND - * ( - * ( user.email = 'test@mail.com' ) OR - * ( self.age <= 30 ) OR - * ( - * ( user.birth != 1986 ) AND - * ( self.age NOT BETWEEN 20 AND 30 ) AND - * ( self.name NOT LIKE '%LE' ) - * ) - * ) - * ) - * ``` - * - order: `user.name ASC,self.id DESC` - * - joining: - * ``` - * INNER JOIN Category ON self.cid = Category.id - * INNER JOIN Country ON self.did = Country.id - * ``` - * - */ - class SQLiteQueryGenerator { - constructor(obj) { - this._where = undefined; - this._fields = undefined; - this._order = undefined; - this._joins = undefined; - this._is_joining = false; - if (obj.joins) { - this._is_joining = true; - this._joins = this.joins(obj.joins); - } - if (obj.where) { - this._where = this.where("$and", obj.where); - } - if (obj.fields) { - this._fields = `( ${obj.fields.map(v => this.infer_field(v)).join(",")} )`; - } - if (obj.order) { - this._order = this.order_by(obj.order); - } - } - infer_field(k) { - if (!this._is_joining || k.indexOf(".") > 0) - return k; - return `self.${k}`; - } - joins(data) { - let joins_arr = []; - for (let k in data) { - let v = data[k]; - let arr = v.split('.'); - if (arr.length != 2) { - throw new Error(__("Other table name parsing error: {0}", v).__()); - } - joins_arr.push(`INNER JOIN ${arr[0]} ON ${this.infer_field(k)} = ${v}`); - } - return joins_arr.join(" "); - } - print() { - console.log(this._fields); - console.log(this._where); - console.log(this._order); - console.log(this._joins); - } - order_by(order) { - if (!Array.isArray(order)) { - throw new Error(__("Invalid type: expect array get {0}", typeof (order)).__()); - } - return order.map((v, _) => { - const arr = v.split('$'); - if (arr.length != 2) { - throw new Error(__("Invalid field order format {0}", v).__()); - } - switch (arr[1]) { - case 'asc': return `${this.infer_field(arr[0])} ASC`; - case 'desc': return `${this.infer_field(arr[0])} DESC`; - default: throw new Error(__("Invalid field order type {0}", v).__()); - } - }).join(","); - } - escape_string(s) { - let regex = /[']/g; - var chunkIndex = regex.lastIndex = 0; - var escapedVal = ''; - var match; - while ((match = regex.exec(s))) { - escapedVal += s.slice(chunkIndex, match.index) + { '\'': '\'\'' }[match[0]]; - chunkIndex = regex.lastIndex; - } - if (chunkIndex === 0) { - // Nothing was escaped - return "'" + s + "'"; - } - if (chunkIndex < s.length) { - return "'" + escapedVal + s.slice(chunkIndex) + "'"; - } - return "'" + escapedVal + "'"; - } - parse_value(v, t) { - if (!t.includes(typeof (v))) { - throw new Error(__("Invalid type: expect [{0}] get {1}", t.join(","), typeof (v)).__()); - } - switch (typeof (v)) { - case 'number': return JSON.stringify(v); - case 'string': return this.escape_string(v); - default: throw new Error(__("Un supported value {0} of type {1}", v, typeof (v)).__()); - } - } - binary(k, v) { - const arr = k.split("$"); - if (arr.length > 2) { - throw new Error(__("Invalid left hand side format: {0}", k).__()); - } - if (arr.length == 2) { - switch (arr[1]) { - case "gt": - return `( ${this.infer_field(arr[0])} > ${this.parse_value(v, ['number'])} )`; - case "gte": - return `( ${this.infer_field(arr[0])} >= ${this.parse_value(v, ['number'])} )`; - case "lt": - return `( ${this.infer_field(arr[0])} < ${this.parse_value(v, ['number'])} )`; - case "lte": - return `( ${this.infer_field(arr[0])} <= ${this.parse_value(v, ['number'])} )`; - case "ne": - return `( ${this.infer_field(arr[0])} != ${this.parse_value(v, ['number', 'string'])} )`; - case "between": - return `( ${this.infer_field(arr[0])} BETWEEN ${this.parse_value(v[0], ['number'])} AND ${this.parse_value(v[1], ['number'])} )`; - case "not_between": - return `( ${this.infer_field(arr[0])} NOT BETWEEN ${this.parse_value(v[0], ['number'])} AND ${this.parse_value(v[1], ['number'])} )`; - case "in": - return `( ${this.infer_field(arr[0])} IN [${this.parse_value(v[0], ['number'])}, ${this.parse_value(v[1], ['number'])}] )`; - case "not_in": - return `( ${this.infer_field(arr[0])} NOT IN [${this.parse_value(v[0], ['number'])}, ${this.parse_value(v[1], ['number'])}] )`; - case "like": - return `( ${this.infer_field(arr[0])} LIKE ${this.parse_value(v, ['string'])} )`; - case "not_like": - return `( ${this.infer_field(arr[0])} NOT LIKE ${this.parse_value(v, ['string'])} )`; - default: throw new Error(__("Unsupported operator `{0}`", arr[1]).__()); - } - } - else { - return `( ${this.infer_field(arr[0])} = ${this.parse_value(v, ['number', 'string'])} )`; - } - } - where(op, obj) { - let join_op = undefined; - switch (op) { - case "$and": - join_op = " AND "; - break; - case "$or": - join_op = " OR "; - break; - default: - throw new Error(__("Invalid operator {0}", op).__()); - } - if (typeof obj !== "object") { - throw new Error(__("Invalid input data for operator {0}", op).__()); - } - let arr = []; - for (let k in obj) { - if (k == "$and" || k == "$or") { - arr.push(this.where(k, obj[k])); - } - else { - arr.push(this.binary(k, obj[k])); - } - } - return `( ${arr.join(join_op)} )`; - } - } class SQLiteDBCore { constructor(path) { if (!SQLiteDBCore.REGISTY) { @@ -588,10 +489,9 @@ var OS; * - other operations are not supported * * `sqlite://remote/path/to/file.db@table_name` refers to the table `table_name` in the database * - meta operation will return fileinfo with table scheme information - * - read operation will read all records by filter defined by the filter operation + * - read operation will read all records by filter defined by the filter as parameters * - write operations will insert a new record - * - rm operation will delete records by filter defined by the filter operation - * - filter operation sets the filter for the table + * - rm operation will delete records by filter as parameters * - other operations are not supported * - `sqlite://remote/path/to/file.db@table_name@id` refers to a records in `table_name` with ID `id` * - read operation will read the current record @@ -599,12 +499,57 @@ var OS; * - rm operation will delete current record * - other operations are not supported * - * Some example of filters: + * Example of filter: * ```ts - * handle.filter = (filter) => { - * filter.fields() - * } - * ``` + * { + * table_name:'contacts'; + * where: { + * id$gte: 10, + * user: "dany'", + * $or: { + * 'user.email': "test@mail.com", + * age$lte: 30, + * $and: { + * 'user.birth$ne': 1986, + * age$not_between: [20,30], + * name$not_like: "%LE" + * } + * } + * }, + * fields: ['name as n', 'id', 'email'], + * order: ['user.name$asc', "id$desc"], + * joins: { + * cid: 'Category.id', + * did: 'Country.id', + * uid: "User.id" + * } + *} + * ``` + * This will generate the followings expressions: + * - `( self.name as n,self.id,self.email )` for fields + * - condition: + * ``` + * ( + * ( contacts.id >= 10 ) AND + * ( contacts.user = 'dany''' ) AND + * ( + * ( user.email = 'test@mail.com' ) OR + * ( contacts.age <= 30 ) OR + * ( + * ( user.birth != 1986 ) AND + * ( contacts.age NOT BETWEEN 20 AND 30 ) AND + * ( contacts.name NOT LIKE '%LE' ) + * ) + * ) + * ) + * ``` + * - order: `user.name ASC,contacts.id DESC` + * - joining: + * ``` + * INNER JOIN Category ON contacts.cid = Category.id + * INNER JOIN Country ON contacts.did = Country.id + * INNER JOIN Country ON contacts.did = Country.id + * ``` * * @class SqliteFileHandle * @extends {BaseFileHandle} diff --git a/SQLiteDB/build/debug/scheme.html b/SQLiteDB/build/debug/scheme.html index c0197cc..3b9996c 100644 --- a/SQLiteDB/build/debug/scheme.html +++ b/SQLiteDB/build/debug/scheme.html @@ -1,5 +1,5 @@ - + @@ -7,12 +7,22 @@ - + - + + + + +
+ + + +
+
+
diff --git a/SQLiteDB/main.ts b/SQLiteDB/main.ts index 7dc8e65..9b87313 100644 --- a/SQLiteDB/main.ts +++ b/SQLiteDB/main.ts @@ -10,7 +10,12 @@ namespace OS { private filehandle: API.VFS.BaseFileHandle; private tbl_list: GUI.tag.ListViewTag; - + private grid_table: GUI.tag.GridViewTag; + private grid_scheme: GUI.tag.GridViewTag; + private container: GUI.tag.TabContainerTag; + private last_max_id: number; + private n_records: number; + private btn_loadmore: GUI.tag.ButtonTag; constructor(args: AppArgumentsType[]) { super("SQLiteDB", args); } @@ -62,48 +67,177 @@ namespace OS { } }) } - private openFile() { - return this.openDialog("FileDialog", { - title: __("Open file"), - mimes: this.meta().mimes - }).then(async (d) => { - this.filehandle = `sqlite://${d.file.path.asFileHandle().genealogy.join("/")}`.asFileHandle(); + private async openFile() { + try { + const d_1 = await this.openDialog("FileDialog",{ + title: __("Open file"), + mimes: this.meta().mimes + }); + this.filehandle=`sqlite://${d_1.file.path.asFileHandle().genealogy.join("/")}`.asFileHandle(); await this.filehandle.onready(); this.list_tables(); - }) - .catch((e) => { - this.error(__("Unable to open database file: {0}", e.toString()), e); - });; + } + catch(e) + { + this.error(__("Unable to open database file: {0}",e.toString()),e); + }; } - private newFile() { - return this.openDialog("FileDialog", { - title: __("Save as"), - file: "Untitled.db" - }).then(async (f) => { - var d; - d = f.file.path.asFileHandle(); - if (f.file.type === "file") { - d = d.parent(); + private async newFile() { + try { + const f = await this.openDialog("FileDialog",{ + title: __("Save as"), + file: "Untitled.db" + }); + var d_1 = f.file.path.asFileHandle(); + if(f.file.type==="file") { + d_1=d_1.parent(); } - const target = `${d.path}/${f.name}`.asFileHandle(); - this.filehandle = `sqlite://${target.genealogy.join("/")}`.asFileHandle(); + const target=`${d_1.path}/${f.name}`.asFileHandle(); + this.filehandle=`sqlite://${target.genealogy.join("/")}`.asFileHandle(); await this.filehandle.onready(); this.list_tables(); - }) - .catch((e) => { - this.error(__("Unable to init database file: {0}", e.toString()), e); - }); + } + catch(e) { + this.error(__("Unable to init database file: {0}",e.toString()),e); + } } main(): void { this.filehandle = undefined; this.tbl_list = this.find("tbl-list") as GUI.tag.ListViewTag; + this.grid_table = this.find("tb-browser") as GUI.tag.GridViewTag; + this.grid_scheme = this.find("sch-browser") as GUI.tag.GridViewTag; + this.grid_table.resizable = true; + this.grid_scheme.resizable = true; + this.grid_scheme.header = [ + { text: __("Field name") }, + { text: __("Field type") }, + ]; + this.btn_loadmore = this.find("bt-load-next") as GUI.tag.ButtonTag; + this.container = this.find("container") as GUI.tag.TabContainerTag; + this.bindKey("ALT-N", () => { + return this.newFile(); + }); + this.bindKey("ALT-O", () => { + return this.openFile(); + }); + + this.container.ontabselect = (e) => { + if(this.container.selectedIndex == 0) + { + if(!this.tbl_list.selectedItem) + return; + const scheme = this.tbl_list.selectedItem.data.handle.info.scheme; + if(!scheme) + return; + const data = []; + for(let k in scheme) + { + data.push([ + { text: k}, + {text: scheme[k]} + ]) + } + this.grid_scheme.rows = data; + } + } + (this.find("bt-add-table") as GUI.tag.ButtonTag).onbtclick = (e) => { + if(!this.filehandle) + { + return this.notify(__("Please open a database file")); + } this.openDialog(new NewTableDialog(), { title: __("Create new table") + }) + .then((data) => { + console.log(data); }); } + this.btn_loadmore.onbtclick = async (e) => { + try + { + await this.load_table(); + } + catch(e) + { + this.error(__("Error reading table: {0}", e.toString()),e); + } + } + this.tbl_list.onlistselect = async (_) => { + try + { + if(!this.tbl_list.selectedItem) + return; + const handle: API.VFS.BaseFileHandle = this.tbl_list.selectedItem.data.handle; + await handle.onready(); + this.last_max_id = 0; + this.grid_table.rows = []; + const headers = + Object.getOwnPropertyNames(handle.info.scheme).map((e)=>{ + return {text: e} + }); + this.grid_table.header = headers; + const records = await handle.read({fields:["COUNT(*)"]}); + this.n_records = records[0]["(COUNT(*))"]; + await this.load_table(); + this.container.selectedIndex = 1; + } + catch(e) + { + this.error(__("Error reading table: {0}", e.toString()),e); + } + } + this.openFile(); + } + + private async load_table() + { + if(this.grid_table.rows.length >= this.n_records) + { + return; + } + if(!this.tbl_list.selectedItem) + return; + const handle: API.VFS.BaseFileHandle = this.tbl_list.selectedItem.data.handle; + await handle.onready(); + const headers = + Object.getOwnPropertyNames(handle.info.scheme).map((e)=>{ + return {text: e} + }); + // read all records + const records = await handle.read({ + where:{ id$gt: this.last_max_id }, + limit: 10 + }); + + if(records && records.length > 0) + { + for(let e of records){ + const row = []; + if(e.id && e.id > this.last_max_id) + { + this.last_max_id = e.id; + } + for(let v in headers) + { + let text:string = e[headers[v].text]; + if(text.length > 100) + { + text = text.substring(0,100); + } + row.push({ + text: text, + record: e + }); + } + this.grid_table.push(row, false); + } + (this.grid_table as any).scroll_to_bottom(); + } + + this.btn_loadmore.text = `${this.grid_table.rows.length}/${this.n_records}`; } } @@ -135,22 +269,36 @@ namespace OS { super.main(); this.container = this.find("container") as HTMLDivElement; (this.find("btnCancel") as GUI.tag.ButtonTag).onbtclick = (e) => this.quit(); - (this.find("btnAdd") as GUI.tag.ButtonTag).onbtclick = (e) => this.addField("", "", true); + (this.find("btnAdd") as GUI.tag.ButtonTag).onbtclick = (e) => this.addField(); $(this.find("wrapper")) $(this.container) .css("overflow-y", "auto"); + this.addField(); (this.find("btnOk") as GUI.tag.ButtonTag).onbtclick = (e) => { - const inputs = $("input", this.scheme) as JQuery; + const input = this.find("txt-tblname") as GUI.tag.InputTag; + + if(!input.value || input.value == "") + { + return this.notify(__("Please enter table name")); + } + + const inputs = $("input", this.container) as JQuery; + const lists = $("afx-list-view", this.container) as JQuery; + if(inputs.length == 0) + { + return this.notify(__("Please define table fields")); + } + let cdata: GenericObject = {}; - for (let i = 0; i < inputs.length; i += 2) { + for (let i = 0; i < inputs.length; i ++) { const key = inputs[i].value.trim(); if (key === "") { - return this.notify(__("Key cannot be empty")); + return this.notify(__("Field name cannot be empty")); } if (cdata[key]) { - return this.notify(__("Duplicate key: {0}", key)); + return this.notify(__("Duplicate field: {0}", key)); } - cdata[key] = inputs[i + 1].value.trim(); + cdata[key] = lists[i].selectedItem.data.text; } if (this.handle) this.handle(cdata); @@ -165,41 +313,35 @@ namespace OS { * @private * @memberof NewTableDialog */ - private addField(key: string, value: string, removable: boolean): void { + private addField(): void { const div = $("
") - .css("width", "100%") .css("display", "flex") .css("flex-direction", "row") .appendTo(this.container); $("") .attr("type", "text") - .css("width", "50%") - .css("height", "25px") - .val(key) + .css("flex", "1") .appendTo(div); - $("") - .css("width", "50%") - .css("height", "25px") + let list = $("") + .css("flex", "1") + .appendTo(div)[0] as GUI.tag.ListViewTag; + list.uify(this.observable); + list.dropdown = true; + list.data = [ + {text:"TEXT"}, + {text:"INTEGER"}, + {text:"REAL"}, + {text:"NUMERIC"}, + ]; + list.selected = 0; + const btn = $(""); + btn[0].uify(undefined); + (btn[0] as GUI.tag.ButtonTag).iconclass = "fa fa-minus"; + btn + .on("click", () => { + div.remove(); + }) .appendTo(div); - if (removable) { - const btn = $(""); - btn[0].uify(undefined); - $("button", btn) - .css("width", "25px") - .css("height", "25px"); - (btn[0] as GUI.tag.ButtonTag).iconclass = "fa fa-minus"; - btn - .on("click", () => { - div.remove(); - }) - .appendTo(div); - } - else { - $("
") - .css("width", "25px") - .appendTo(div); - } - } } @@ -208,23 +350,19 @@ namespace OS { * Scheme definition */ NewTableDialog.scheme = `\ - - -
- -
- -
- - -
- - -
-
-
-
-
-
`; + + + + +
+ + +
+ + +
+
+
+
`; } } \ No newline at end of file diff --git a/SQLiteDB/scheme.html b/SQLiteDB/scheme.html index c0197cc..3b9996c 100644 --- a/SQLiteDB/scheme.html +++ b/SQLiteDB/scheme.html @@ -1,5 +1,5 @@ - + @@ -7,12 +7,22 @@ - + - + + + + +
+ + + +
+
+
diff --git a/libantosdk/README.md b/libantosdk/README.md index 94dbc21..5772d60 100644 --- a/libantosdk/README.md +++ b/libantosdk/README.md @@ -2,6 +2,7 @@ AntOSDK: development API for AntOS based applications/projects ## Change logs +- 0.0.18: Add some public API on grid view - 0.0.17: Use lastest AntOS d.ts file - 0.0.16: Fix jquery encoding error - 0.0.15: App name differ from libname, update AntOS API diff --git a/libantosdk/build/debug/README.md b/libantosdk/build/debug/README.md index 94dbc21..5772d60 100644 --- a/libantosdk/build/debug/README.md +++ b/libantosdk/build/debug/README.md @@ -2,6 +2,7 @@ AntOSDK: development API for AntOS based applications/projects ## Change logs +- 0.0.18: Add some public API on grid view - 0.0.17: Use lastest AntOS d.ts file - 0.0.16: Fix jquery encoding error - 0.0.15: App name differ from libname, update AntOS API diff --git a/libantosdk/build/debug/core/ts/antos.d.ts b/libantosdk/build/debug/core/ts/antos.d.ts index 73a82a3..efc6b63 100644 --- a/libantosdk/build/debug/core/ts/antos.d.ts +++ b/libantosdk/build/debug/core/ts/antos.d.ts @@ -475,6 +475,7 @@ declare namespace OS { * @extends {BaseDialog} */ class BasicDialog extends BaseDialog { + ['constructor']: typeof BasicDialog; /** * Placeholder for the UI scheme to be rendered. This can * be either the string definition of the scheme or @@ -4421,6 +4422,14 @@ declare namespace OS { * @memberof ListViewTag */ private _selectedItems; + /** + * The anchor element that the list view positioned on + * This is helpful when rendering dropdown list + * @private + * @type{HTMLElement} + * @memberof ListViewTag + */ + private _anchor; /** * Data placeholder of the list * @@ -5539,6 +5548,18 @@ declare namespace OS { * @memberof LabelTag */ set iconclass(v: string); + /** + * Set the CSS class of the label icon on the right side + * + * @memberof LabelTag + */ + set iconclass_end(v: string); + /** + * Set the CSS class of the label icon on the right side + * + * @memberof LabelTag + */ + set iconclass$(v: string); /** * Setter: Set the text of the label * @@ -6295,13 +6316,25 @@ declare namespace OS { * @memberof GridViewTag */ delete(row: GridRowTag): void; + /** + * Scroll the grid view to bottom + * + * @memberof GridViewTag + */ + scroll_to_bottom(): void; + /** + * Scroll the grid view to top + * + * @memberof GridViewTag + */ + scroll_to_top(): void; /** * Push a row to the grid * * @param {GenericObject[]} row list of cell data * @param {boolean} flag indicates where the row is add to beginning or end * of the row - * @memberof GridViewTags + * @memberof GridViewTag */ push(row: GenericObject[], flag: boolean): void; /** @@ -6622,6 +6655,19 @@ declare namespace OS { * @memberof ButtonTag */ set iconclass(v: string); + /** + * Set the icon class on the right side of the button, this property + * allows to style the button icon using CSS + * + * @memberof ButtonTag + */ + set iconclass$(v: string); + /** + * Set the CSS class of the label icon on the right side + * + * @memberof ButtonTag + */ + set iconclass_end(v: string); /** * Setter: Set the text of the button * diff --git a/libantosdk/build/debug/package.json b/libantosdk/build/debug/package.json index 17289e9..da101de 100644 --- a/libantosdk/build/debug/package.json +++ b/libantosdk/build/debug/package.json @@ -7,7 +7,7 @@ "author": "Xuan Sang LE", "email": "mrsang@iohub.dev" }, - "version": "0.0.17-a", + "version": "0.0.18-a", "category": "Development", "iconclass": "fa fa-cog", "mimes": [ diff --git a/libantosdk/build/release/libantosdk.zip b/libantosdk/build/release/libantosdk.zip index 6043e36d5fbbc2985fb746a6040f1196a042fe03..ee93e6dc93baae0b8af93437a9100cb99595e101 100644 GIT binary patch delta 37441 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

SG(42;Smm}GP!9)U%mwPACcG_fD+x`HB_q5 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 T;R%0;^@)Mcvq delta 37353 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�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[]} row list of cell data * @param {boolean} flag indicates where the row is add to beginning or end * of the row - * @memberof GridViewTags + * @memberof GridViewTag */ push(row: GenericObject[], flag: boolean): void; /** @@ -6622,6 +6655,19 @@ declare namespace OS { * @memberof ButtonTag */ set iconclass(v: string); + /** + * Set the icon class on the right side of the button, this property + * allows to style the button icon using CSS + * + * @memberof ButtonTag + */ + set iconclass$(v: string); + /** + * Set the CSS class of the label icon on the right side + * + * @memberof ButtonTag + */ + set iconclass_end(v: string); /** * Setter: Set the text of the button * diff --git a/libantosdk/package.json b/libantosdk/package.json index 17289e9..da101de 100644 --- a/libantosdk/package.json +++ b/libantosdk/package.json @@ -7,7 +7,7 @@ "author": "Xuan Sang LE", "email": "mrsang@iohub.dev" }, - "version": "0.0.17-a", + "version": "0.0.18-a", "category": "Development", "iconclass": "fa fa-cog", "mimes": [ diff --git a/packages.json b/packages.json index 735d39e..0b1fc65 100644 --- a/packages.json +++ b/packages.json @@ -225,7 +225,7 @@ "description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/2.0.x/libantosdk/README.md", "category": "Development", "author": "Xuan Sang LE", - "version": "0.0.17-a", + "version": "0.0.18-a", "dependencies": [], "download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/2.0.x/libantosdk/build/release/libantosdk.zip" },