mirror of
https://github.com/antos-rde/antosdk-apps.git
synced 2024-12-24 11:18:21 +01:00
SQLiteDB: first working version
This commit is contained in:
parent
cf21ef60e0
commit
04050f124f
@ -164,25 +164,27 @@ namespace OS {
|
||||
return this.request(rq);
|
||||
}
|
||||
|
||||
insert(table_name:string, record: GenericObject<any>): Promise<any>
|
||||
insert(table_name:string, record: GenericObject<any>, pk:string): Promise<any>
|
||||
{
|
||||
let rq = {
|
||||
action: 'insert',
|
||||
args: {
|
||||
table_name,
|
||||
record
|
||||
record,
|
||||
pk
|
||||
}
|
||||
}
|
||||
return this.request(rq);
|
||||
}
|
||||
|
||||
update(table_name:string, record: GenericObject<any>): Promise<any>
|
||||
update(table_name:string, record: GenericObject<any>, pk:string ): Promise<any>
|
||||
{
|
||||
let rq = {
|
||||
action: 'update',
|
||||
args: {
|
||||
table_name,
|
||||
record
|
||||
record,
|
||||
pk
|
||||
}
|
||||
}
|
||||
return this.request(rq);
|
||||
@ -317,20 +319,33 @@ namespace OS {
|
||||
try {
|
||||
await this._handle.init();
|
||||
|
||||
const d = {result: this._handle.fileinfo(), error: false};
|
||||
let d = {
|
||||
result: {
|
||||
file:this._handle.fileinfo(),
|
||||
schema: undefined
|
||||
}, error: false};
|
||||
if(this._table_name)
|
||||
{
|
||||
const data = await this._handle.get_table_scheme(this._table_name);
|
||||
if(data.length == 0)
|
||||
{
|
||||
d.result.scheme = undefined
|
||||
d.result.schema = undefined
|
||||
}
|
||||
else
|
||||
{
|
||||
d.result.scheme = {}
|
||||
d.result.schema = {
|
||||
fields: [],
|
||||
types: {},
|
||||
pk: undefined
|
||||
}
|
||||
d.result.schema.fields = data.map(e=>e.name);
|
||||
for(let v of data)
|
||||
{
|
||||
d.result.scheme[v.name] = v.type;
|
||||
d.result.schema.types[v.name] = v.type;
|
||||
if(v.pk)
|
||||
{
|
||||
d.result.schema.pk = v.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -358,7 +373,7 @@ namespace OS {
|
||||
protected _rd(user_data: any): Promise<any> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try{
|
||||
if(this._table_name && ! this.info.scheme)
|
||||
if(this._table_name && ! this.info.schema)
|
||||
{
|
||||
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
||||
}
|
||||
@ -422,17 +437,18 @@ namespace OS {
|
||||
{
|
||||
throw new Error(__("No data to submit to remote database, please check the `cache` field").__());
|
||||
}
|
||||
await this.onready();
|
||||
if(this._id && this._table_name)
|
||||
{
|
||||
this.cache.id = this._id;
|
||||
const ret = await this._handle.update(this._table_name, this.cache);
|
||||
const ret = await this._handle.update(this._table_name, this.cache, this.info.schema.pk);
|
||||
resolve({result:ret, error: false});
|
||||
return
|
||||
}
|
||||
|
||||
if(this._table_name)
|
||||
{
|
||||
const ret = await this._handle.insert(this._table_name, this.cache);
|
||||
const ret = await this._handle.insert(this._table_name, this.cache, this.info.schema.pk);
|
||||
resolve({result:ret, error: false});
|
||||
return
|
||||
}
|
||||
@ -458,7 +474,7 @@ namespace OS {
|
||||
protected _rm(user_data: any): Promise<RequestResult> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if(this._table_name && ! this.info.scheme)
|
||||
if(this._table_name && ! this.info.schema)
|
||||
{
|
||||
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
# SQLiteDB
|
||||
|
||||
"mimes":["application/vnd.sqlite3"],
|
||||
This package contains the SQLiteDB API binding for AntOS applications
|
||||
and a simple sqlite3 browser application that uses the library as reference
|
||||
|
||||
Note: in AntOS, file with extension `.db` is considered as sqlite3 database
|
||||
file and has the following mimetype `application/vnd.sqlite3`. Applications
|
||||
shall use this mime in `package.json`
|
||||
|
||||
## Change logs
|
||||
- v0.1.0a: initial version with functioning library binding
|
||||
|
@ -44,7 +44,7 @@ handle.update = function(data)
|
||||
local tb = {}
|
||||
local gen = SQLQueryGenerator:new({})
|
||||
for k,v in pairs(data.record) do
|
||||
if k ~= "id" then
|
||||
if k ~= data.pk then
|
||||
table.insert(tb, string.format("%s=%s", k, gen:parse_value(v, {[type(v)] = true})))
|
||||
end
|
||||
end
|
||||
@ -92,7 +92,7 @@ handle.insert = function(data)
|
||||
local vals = {}
|
||||
local gen = SQLQueryGenerator:new({})
|
||||
for k,v in pairs(data.record) do
|
||||
if k ~= "id" then
|
||||
if k ~= data.pk then
|
||||
table.insert(keys,k)
|
||||
table.insert(vals,gen:parse_value(v, {[type(v)] = true}))
|
||||
end
|
||||
@ -119,7 +119,9 @@ handle.create_table = function(data)
|
||||
end
|
||||
local tb = {}
|
||||
for k,v in pairs(data.scheme) do
|
||||
table.insert(tb, k.." "..v)
|
||||
if k ~= "id" then
|
||||
table.insert(tb, k.." "..v)
|
||||
end
|
||||
end
|
||||
local sql = string.format("CREATE TABLE IF NOT EXISTS %s(id INTEGER PRIMARY KEY,%s)", data.table_name, table.concat(tb,","))
|
||||
LOG_DEBUG("Execute query: [%s]", sql)
|
||||
@ -186,7 +188,7 @@ handle.table_scheme = function(data)
|
||||
if not db then
|
||||
return error("table_scheme: Unable to open sqlite db file")
|
||||
end
|
||||
local sql = string.format("SELECT p.name, p.type FROM sqlite_master AS m JOIN pragma_table_info(m.name) AS p WHERE m.type ='table' AND m.name = '%s'", data.table_name)
|
||||
local sql = string.format("SELECT p.name, p.type, p.pk FROM sqlite_master AS m JOIN pragma_table_info(m.name) AS p WHERE m.type ='table' AND m.name = '%s'", data.table_name)
|
||||
local ret, err = sqlite.query(db, sql);
|
||||
sqlite.dbclose(db)
|
||||
if not ret then
|
||||
|
@ -1,3 +1,11 @@
|
||||
# SQLiteDB
|
||||
|
||||
"mimes":["application/vnd.sqlite3"],
|
||||
This package contains the SQLiteDB API binding for AntOS applications
|
||||
and a simple sqlite3 browser application that uses the library as reference
|
||||
|
||||
Note: in AntOS, file with extension `.db` is considered as sqlite3 database
|
||||
file and has the following mimetype `application/vnd.sqlite3`. Applications
|
||||
shall use this mime in `package.json`
|
||||
|
||||
## Change logs
|
||||
- v0.1.0a: initial version with functioning library binding
|
||||
|
@ -44,7 +44,7 @@ handle.update = function(data)
|
||||
local tb = {}
|
||||
local gen = SQLQueryGenerator:new({})
|
||||
for k,v in pairs(data.record) do
|
||||
if k ~= "id" then
|
||||
if k ~= data.pk then
|
||||
table.insert(tb, string.format("%s=%s", k, gen:parse_value(v, {[type(v)] = true})))
|
||||
end
|
||||
end
|
||||
@ -92,7 +92,7 @@ handle.insert = function(data)
|
||||
local vals = {}
|
||||
local gen = SQLQueryGenerator:new({})
|
||||
for k,v in pairs(data.record) do
|
||||
if k ~= "id" then
|
||||
if k ~= data.pk then
|
||||
table.insert(keys,k)
|
||||
table.insert(vals,gen:parse_value(v, {[type(v)] = true}))
|
||||
end
|
||||
@ -119,7 +119,9 @@ handle.create_table = function(data)
|
||||
end
|
||||
local tb = {}
|
||||
for k,v in pairs(data.scheme) do
|
||||
table.insert(tb, k.." "..v)
|
||||
if k ~= "id" then
|
||||
table.insert(tb, k.." "..v)
|
||||
end
|
||||
end
|
||||
local sql = string.format("CREATE TABLE IF NOT EXISTS %s(id INTEGER PRIMARY KEY,%s)", data.table_name, table.concat(tb,","))
|
||||
LOG_DEBUG("Execute query: [%s]", sql)
|
||||
@ -186,7 +188,7 @@ handle.table_scheme = function(data)
|
||||
if not db then
|
||||
return error("table_scheme: Unable to open sqlite db file")
|
||||
end
|
||||
local sql = string.format("SELECT p.name, p.type FROM sqlite_master AS m JOIN pragma_table_info(m.name) AS p WHERE m.type ='table' AND m.name = '%s'", data.table_name)
|
||||
local sql = string.format("SELECT p.name, p.type, p.pk FROM sqlite_master AS m JOIN pragma_table_info(m.name) AS p WHERE m.type ='table' AND m.name = '%s'", data.table_name)
|
||||
local ret, err = sqlite.query(db, sql);
|
||||
sqlite.dbclose(db)
|
||||
if not ret then
|
||||
|
@ -130,22 +130,24 @@ var OS;
|
||||
};
|
||||
return this.request(rq);
|
||||
}
|
||||
insert(table_name, record) {
|
||||
insert(table_name, record, pk) {
|
||||
let rq = {
|
||||
action: 'insert',
|
||||
args: {
|
||||
table_name,
|
||||
record
|
||||
record,
|
||||
pk
|
||||
}
|
||||
};
|
||||
return this.request(rq);
|
||||
}
|
||||
update(table_name, record) {
|
||||
update(table_name, record, pk) {
|
||||
let rq = {
|
||||
action: 'update',
|
||||
args: {
|
||||
table_name,
|
||||
record
|
||||
record,
|
||||
pk
|
||||
}
|
||||
};
|
||||
return this.request(rq);
|
||||
@ -270,16 +272,29 @@ var OS;
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
await this._handle.init();
|
||||
const d = { result: this._handle.fileinfo(), error: false };
|
||||
let d = {
|
||||
result: {
|
||||
file: this._handle.fileinfo(),
|
||||
schema: undefined
|
||||
}, error: false
|
||||
};
|
||||
if (this._table_name) {
|
||||
const data = await this._handle.get_table_scheme(this._table_name);
|
||||
if (data.length == 0) {
|
||||
d.result.scheme = undefined;
|
||||
d.result.schema = undefined;
|
||||
}
|
||||
else {
|
||||
d.result.scheme = {};
|
||||
d.result.schema = {
|
||||
fields: [],
|
||||
types: {},
|
||||
pk: undefined
|
||||
};
|
||||
d.result.schema.fields = data.map(e => e.name);
|
||||
for (let v of data) {
|
||||
d.result.scheme[v.name] = v.type;
|
||||
d.result.schema.types[v.name] = v.type;
|
||||
if (v.pk) {
|
||||
d.result.schema.pk = v.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -307,7 +322,7 @@ var OS;
|
||||
_rd(user_data) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (this._table_name && !this.info.scheme) {
|
||||
if (this._table_name && !this.info.schema) {
|
||||
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
||||
}
|
||||
if (!this._table_name) {
|
||||
@ -361,14 +376,15 @@ var OS;
|
||||
if (!this.cache) {
|
||||
throw new Error(__("No data to submit to remote database, please check the `cache` field").__());
|
||||
}
|
||||
await this.onready();
|
||||
if (this._id && this._table_name) {
|
||||
this.cache.id = this._id;
|
||||
const ret = await this._handle.update(this._table_name, this.cache);
|
||||
const ret = await this._handle.update(this._table_name, this.cache, this.info.schema.pk);
|
||||
resolve({ result: ret, error: false });
|
||||
return;
|
||||
}
|
||||
if (this._table_name) {
|
||||
const ret = await this._handle.insert(this._table_name, this.cache);
|
||||
const ret = await this._handle.insert(this._table_name, this.cache, this.info.schema.pk);
|
||||
resolve({ result: ret, error: false });
|
||||
return;
|
||||
}
|
||||
@ -392,7 +408,7 @@ var OS;
|
||||
_rm(user_data) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (this._table_name && !this.info.scheme) {
|
||||
if (this._table_name && !this.info.schema) {
|
||||
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
||||
}
|
||||
if (!this._table_name) {
|
||||
|
@ -6,12 +6,12 @@ var OS;
|
||||
(function (application) {
|
||||
/**
|
||||
*
|
||||
* @class SQLiteDB
|
||||
* @class SQLiteDBBrowser
|
||||
* @extends {BaseApplication}
|
||||
*/
|
||||
class SQLiteDB extends application.BaseApplication {
|
||||
class SQLiteDBBrowser extends application.BaseApplication {
|
||||
constructor(args) {
|
||||
super("SQLiteDB", args);
|
||||
super("SQLiteDBBrowser", args);
|
||||
}
|
||||
menu() {
|
||||
return [
|
||||
@ -19,12 +19,12 @@ var OS;
|
||||
text: "__(File)",
|
||||
nodes: [
|
||||
{
|
||||
text: "__(New)",
|
||||
text: "__(New database)",
|
||||
dataid: "new",
|
||||
shortcut: 'A-N'
|
||||
},
|
||||
{
|
||||
text: "__(Open)",
|
||||
text: "__(Open database)",
|
||||
dataid: "open",
|
||||
shortcut: 'A-O'
|
||||
},
|
||||
@ -53,7 +53,7 @@ var OS;
|
||||
}
|
||||
this.tbl_list.data = list;
|
||||
if (list.length > 0) {
|
||||
this.tbl_list.selected = 0;
|
||||
this.tbl_list.selected = list.length - 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -114,29 +114,65 @@ var OS;
|
||||
if (this.container.selectedIndex == 0) {
|
||||
if (!this.tbl_list.selectedItem)
|
||||
return;
|
||||
const scheme = this.tbl_list.selectedItem.data.handle.info.scheme;
|
||||
if (!scheme)
|
||||
const schema = this.tbl_list.selectedItem.data.handle.info.schema;
|
||||
if (!schema)
|
||||
return;
|
||||
const data = [];
|
||||
for (let k in scheme) {
|
||||
for (let k in schema.types) {
|
||||
data.push([
|
||||
{ text: k },
|
||||
{ text: scheme[k] }
|
||||
{ text: schema.types[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.find("bt-rm-table").onbtclick = async (e) => {
|
||||
try {
|
||||
if (!this.filehandle) {
|
||||
return this.notify(__("Please open a database file"));
|
||||
}
|
||||
if (this.tbl_list.selectedItem == undefined) {
|
||||
return;
|
||||
}
|
||||
const table = this.tbl_list.selectedItem.data.name;
|
||||
const ret = await this.openDialog("YesNoDialog", {
|
||||
title: __("Confirm delete?"),
|
||||
text: __("Do you realy want to delete table: {0}", table)
|
||||
});
|
||||
if (ret) {
|
||||
await this.filehandle.remove(table);
|
||||
this.list_tables();
|
||||
}
|
||||
}
|
||||
this.openDialog(new NewTableDialog(), {
|
||||
title: __("Create new table")
|
||||
})
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
catch (e) {
|
||||
this.error(__("Unable to execute action table delete: {0}", e.toString()), e);
|
||||
}
|
||||
};
|
||||
this.find("bt-add-table").onbtclick = async (e) => {
|
||||
try {
|
||||
if (!this.filehandle) {
|
||||
return this.notify(__("Please open a database file"));
|
||||
}
|
||||
const data = await this.openDialog(new NewTableDialog(), {
|
||||
title: __("Create new table")
|
||||
});
|
||||
this.filehandle.cache = data.schema;
|
||||
await this.filehandle.write(data.name);
|
||||
this.list_tables();
|
||||
}
|
||||
catch (e) {
|
||||
this.error(__("Unable to create table: {0}", e.toString()), e);
|
||||
}
|
||||
};
|
||||
this.find("btn-edit-record").onbtclick = async (e) => {
|
||||
this.edit_record();
|
||||
};
|
||||
this.find("btn-add-record").onbtclick = async (e) => {
|
||||
this.add_record();
|
||||
};
|
||||
this.find("btn-delete-record").onbtclick = async (e) => {
|
||||
this.remove_record();
|
||||
};
|
||||
this.btn_loadmore.onbtclick = async (e) => {
|
||||
try {
|
||||
@ -153,13 +189,14 @@ var OS;
|
||||
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) => {
|
||||
const headers = handle.info.schema.fields.map((e) => {
|
||||
return { text: e };
|
||||
});
|
||||
this.grid_table.header = headers;
|
||||
this.grid_table.rows = [];
|
||||
const records = await handle.read({ fields: ["COUNT(*)"] });
|
||||
this.n_records = records[0]["(COUNT(*))"];
|
||||
this.btn_loadmore.text = `0/${this.n_records}`;
|
||||
await this.load_table();
|
||||
this.container.selectedIndex = 1;
|
||||
}
|
||||
@ -167,24 +204,119 @@ var OS;
|
||||
this.error(__("Error reading table: {0}", e.toString()), e);
|
||||
}
|
||||
};
|
||||
this.grid_table.oncelldbclick = async (e) => {
|
||||
this.edit_record();
|
||||
};
|
||||
this.openFile();
|
||||
}
|
||||
async add_record() {
|
||||
try {
|
||||
const table_handle = this.tbl_list.selectedItem;
|
||||
if (!table_handle) {
|
||||
return;
|
||||
}
|
||||
const file_hd = table_handle.data.handle;
|
||||
const schema = table_handle.data.handle.info.schema;
|
||||
const model = {};
|
||||
for (let k in schema.types) {
|
||||
if (["INTEGER", "REAL", "NUMERIC"].includes(schema.types[k])) {
|
||||
model[k] = 0;
|
||||
}
|
||||
else {
|
||||
model[k] = "";
|
||||
}
|
||||
}
|
||||
console.log(model);
|
||||
const data = await this.openDialog(new RecordEditDialog(), {
|
||||
title: __("New record"),
|
||||
schema: schema,
|
||||
record: model
|
||||
});
|
||||
file_hd.cache = data;
|
||||
await file_hd.write(undefined);
|
||||
this.n_records += 1;
|
||||
await this.load_table();
|
||||
}
|
||||
catch (e) {
|
||||
this.error(__("Error edit/view record: {0}", e.toString()), e);
|
||||
}
|
||||
}
|
||||
async remove_record() {
|
||||
try {
|
||||
const cell = this.grid_table.selectedCell;
|
||||
const row = this.grid_table.selectedRow;
|
||||
const table_handle = this.tbl_list.selectedItem;
|
||||
if (!cell || !table_handle) {
|
||||
return;
|
||||
}
|
||||
const pk_id = cell.data.record[table_handle.data.handle.info.schema.pk];
|
||||
const ret = await this.openDialog("YesNoDialog", {
|
||||
title: __("Delete record"),
|
||||
text: __("Do you realy want to delete record {0}", pk_id)
|
||||
});
|
||||
if (!ret) {
|
||||
return;
|
||||
}
|
||||
const file_hd = `${table_handle.data.handle.path}@${pk_id}`.asFileHandle();
|
||||
await file_hd.remove();
|
||||
this.n_records--;
|
||||
// remove the target row
|
||||
this.grid_table.delete(row);
|
||||
this.btn_loadmore.text = `${this.grid_table.rows.length}/${this.n_records}`;
|
||||
}
|
||||
catch (e) {
|
||||
this.error(__("Error deleting record: {0}", e.toString()), e);
|
||||
}
|
||||
}
|
||||
async edit_record() {
|
||||
try {
|
||||
const cell = this.grid_table.selectedCell;
|
||||
const row = this.grid_table.selectedRow;
|
||||
const table_handle = this.tbl_list.selectedItem;
|
||||
if (!cell || !table_handle) {
|
||||
return;
|
||||
}
|
||||
const data = await this.openDialog(new RecordEditDialog(), {
|
||||
title: __("View/edit record"),
|
||||
schema: table_handle.data.handle.info.schema,
|
||||
record: cell.data.record
|
||||
});
|
||||
const pk_id = cell.data.record[table_handle.data.handle.info.schema.pk];
|
||||
const file_hd = `${table_handle.data.handle.path}@${pk_id}`.asFileHandle();
|
||||
file_hd.cache = data;
|
||||
await file_hd.write(undefined);
|
||||
const row_data = [];
|
||||
for (let k of file_hd.info.schema.fields) {
|
||||
let text = data[k];
|
||||
if (text.length > 100) {
|
||||
text = text.substring(0, 100);
|
||||
}
|
||||
row_data.push({
|
||||
text: text,
|
||||
record: data
|
||||
});
|
||||
}
|
||||
row.data = row_data;
|
||||
}
|
||||
catch (e) {
|
||||
this.error(__("Error edit/view record: {0}", e.toString()), e);
|
||||
}
|
||||
}
|
||||
async load_table() {
|
||||
if (this.grid_table.rows.length >= this.n_records) {
|
||||
if (this.grid_table.rows && 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) => {
|
||||
const headers = handle.info.schema.fields.map((e) => {
|
||||
return { text: e };
|
||||
});
|
||||
// read all records
|
||||
const records = await handle.read({
|
||||
where: { id$gt: this.last_max_id },
|
||||
limit: 10
|
||||
});
|
||||
const filter = { where: {}, limit: 10 };
|
||||
filter.where[`${handle.info.schema.pk}\$gt`] = this.last_max_id;
|
||||
const records = await handle.read(filter);
|
||||
if (records && records.length > 0) {
|
||||
for (let e of records) {
|
||||
const row = [];
|
||||
@ -208,7 +340,10 @@ var OS;
|
||||
this.btn_loadmore.text = `${this.grid_table.rows.length}/${this.n_records}`;
|
||||
}
|
||||
}
|
||||
application.SQLiteDB = SQLiteDB;
|
||||
application.SQLiteDBBrowser = SQLiteDBBrowser;
|
||||
SQLiteDBBrowser.dependencies = [
|
||||
"pkg://SQLiteDB/libsqlite.js"
|
||||
];
|
||||
class NewTableDialog extends OS.GUI.BasicDialog {
|
||||
/**
|
||||
* Creates an instance of NewTableDialog.
|
||||
@ -227,7 +362,6 @@ var OS;
|
||||
this.container = this.find("container");
|
||||
this.find("btnCancel").onbtclick = (e) => this.quit();
|
||||
this.find("btnAdd").onbtclick = (e) => this.addField();
|
||||
$(this.find("wrapper"));
|
||||
$(this.container)
|
||||
.css("overflow-y", "auto");
|
||||
this.addField();
|
||||
@ -236,6 +370,7 @@ var OS;
|
||||
if (!input.value || input.value == "") {
|
||||
return this.notify(__("Please enter table name"));
|
||||
}
|
||||
const tblname = input.value;
|
||||
const inputs = $("input", this.container);
|
||||
const lists = $("afx-list-view", this.container);
|
||||
if (inputs.length == 0) {
|
||||
@ -253,7 +388,7 @@ var OS;
|
||||
cdata[key] = lists[i].selectedItem.data.text;
|
||||
}
|
||||
if (this.handle)
|
||||
this.handle(cdata);
|
||||
this.handle({ name: tblname, schema: cdata });
|
||||
this.quit();
|
||||
};
|
||||
}
|
||||
@ -304,13 +439,78 @@ var OS;
|
||||
<afx-label text="__(Fields in table:)" data-height="30"></afx-label>
|
||||
<div data-id="container" style="position:relative;"></div>
|
||||
<afx-hbox data-height="35">
|
||||
<afx-button data-id = "btnAdd" iconclass="fa fa-plus" data-width = "35" ></afx-button>
|
||||
<afx-button data-id = "btnAdd" iconclass="fa fa-plus" data-width = "content" ></afx-button>
|
||||
<div style = "text-align: right;">
|
||||
<afx-button data-id = "btnOk" text = "__(Ok)"></afx-button>
|
||||
<afx-button data-id = "btnCancel" text = "__(Cancel)"></afx-button>
|
||||
</div>
|
||||
</afx-hbox>
|
||||
</afx-vbox>
|
||||
</afx-app-window>`;
|
||||
class RecordEditDialog extends OS.GUI.BasicDialog {
|
||||
/**
|
||||
* Creates an instance of RecordEditDialog.
|
||||
* @memberof RecordEditDialog
|
||||
*/
|
||||
constructor() {
|
||||
super("RecordEditDialog");
|
||||
}
|
||||
/**
|
||||
* Main entry point
|
||||
*
|
||||
* @memberof RecordEditDialog
|
||||
*/
|
||||
main() {
|
||||
super.main();
|
||||
this.container = this.find("container");
|
||||
this.find("btnCancel").onbtclick = (e) => this.quit();
|
||||
$(this.container)
|
||||
.css("overflow-y", "auto");
|
||||
if (!this.data || !this.data.schema) {
|
||||
throw new Error(__("No data provided for dialog").__());
|
||||
}
|
||||
for (let k in this.data.schema.types) {
|
||||
const input = $("<afx-input>").appendTo(this.container)[0];
|
||||
input.uify(this.observable);
|
||||
input.label = k;
|
||||
if (k == this.data.schema.pk) {
|
||||
input.disable = true;
|
||||
}
|
||||
if (this.data.schema.types[k] == "TEXT") {
|
||||
input.verbose = true;
|
||||
$(input).css("height", "100px");
|
||||
}
|
||||
if (this.data.record[k] != undefined) {
|
||||
input.value = this.data.record[k];
|
||||
}
|
||||
}
|
||||
this.find("btnOk").onbtclick = (e) => {
|
||||
const inputs = $("afx-input", this.container);
|
||||
const data = {};
|
||||
for (let input of inputs) {
|
||||
data[input.label.__()] = input.value;
|
||||
}
|
||||
if (this.handle)
|
||||
this.handle(data);
|
||||
this.quit();
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Scheme definition
|
||||
*/
|
||||
RecordEditDialog.scheme = `\
|
||||
<afx-app-window width='550' height='500'>
|
||||
<afx-vbox padding = "5">
|
||||
<div data-id="container" style="row-gap: 5px;"></div>
|
||||
<afx-hbox data-height="35">
|
||||
<div></div>
|
||||
<div data-width="content">
|
||||
<afx-button data-id = "btnOk" text = "__(Ok)"></afx-button>
|
||||
<afx-button data-id = "btnCancel" text = "__(Cancel)"></afx-button>
|
||||
</div>
|
||||
</afx-hbox>
|
||||
</afx-vbox>
|
||||
</afx-app-window>`;
|
||||
})(application = OS.application || (OS.application = {}));
|
||||
})(OS || (OS = {}));
|
||||
@ -447,22 +647,24 @@ var OS;
|
||||
};
|
||||
return this.request(rq);
|
||||
}
|
||||
insert(table_name, record) {
|
||||
insert(table_name, record, pk) {
|
||||
let rq = {
|
||||
action: 'insert',
|
||||
args: {
|
||||
table_name,
|
||||
record
|
||||
record,
|
||||
pk
|
||||
}
|
||||
};
|
||||
return this.request(rq);
|
||||
}
|
||||
update(table_name, record) {
|
||||
update(table_name, record, pk) {
|
||||
let rq = {
|
||||
action: 'update',
|
||||
args: {
|
||||
table_name,
|
||||
record
|
||||
record,
|
||||
pk
|
||||
}
|
||||
};
|
||||
return this.request(rq);
|
||||
@ -587,16 +789,29 @@ var OS;
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
await this._handle.init();
|
||||
const d = { result: this._handle.fileinfo(), error: false };
|
||||
let d = {
|
||||
result: {
|
||||
file: this._handle.fileinfo(),
|
||||
schema: undefined
|
||||
}, error: false
|
||||
};
|
||||
if (this._table_name) {
|
||||
const data = await this._handle.get_table_scheme(this._table_name);
|
||||
if (data.length == 0) {
|
||||
d.result.scheme = undefined;
|
||||
d.result.schema = undefined;
|
||||
}
|
||||
else {
|
||||
d.result.scheme = {};
|
||||
d.result.schema = {
|
||||
fields: [],
|
||||
types: {},
|
||||
pk: undefined
|
||||
};
|
||||
d.result.schema.fields = data.map(e => e.name);
|
||||
for (let v of data) {
|
||||
d.result.scheme[v.name] = v.type;
|
||||
d.result.schema.types[v.name] = v.type;
|
||||
if (v.pk) {
|
||||
d.result.schema.pk = v.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -624,7 +839,7 @@ var OS;
|
||||
_rd(user_data) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (this._table_name && !this.info.scheme) {
|
||||
if (this._table_name && !this.info.schema) {
|
||||
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
||||
}
|
||||
if (!this._table_name) {
|
||||
@ -678,14 +893,15 @@ var OS;
|
||||
if (!this.cache) {
|
||||
throw new Error(__("No data to submit to remote database, please check the `cache` field").__());
|
||||
}
|
||||
await this.onready();
|
||||
if (this._id && this._table_name) {
|
||||
this.cache.id = this._id;
|
||||
const ret = await this._handle.update(this._table_name, this.cache);
|
||||
const ret = await this._handle.update(this._table_name, this.cache, this.info.schema.pk);
|
||||
resolve({ result: ret, error: false });
|
||||
return;
|
||||
}
|
||||
if (this._table_name) {
|
||||
const ret = await this._handle.insert(this._table_name, this.cache);
|
||||
const ret = await this._handle.insert(this._table_name, this.cache, this.info.schema.pk);
|
||||
resolve({ result: ret, error: false });
|
||||
return;
|
||||
}
|
||||
@ -709,7 +925,7 @@ var OS;
|
||||
_rm(user_data) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (this._table_name && !this.info.scheme) {
|
||||
if (this._table_name && !this.info.schema) {
|
||||
throw new Error(__("Table `{0}` does not exists in database: {1}", this._table_name, this.path).__());
|
||||
}
|
||||
if (!this._table_name) {
|
||||
|
@ -1,16 +1,16 @@
|
||||
{
|
||||
"pkgname": "SQLiteDB",
|
||||
"app":"SQLiteDB",
|
||||
"name":"SQLite3 VFS API",
|
||||
"app":"SQLiteDBBrowser",
|
||||
"name":"SQLite3 Browser",
|
||||
"description":"API for manipulate SQLite database as VFS handle",
|
||||
"info":{
|
||||
"author": "",
|
||||
"email": ""
|
||||
"author": "Dany LE",
|
||||
"email": "mrsang@iohub.dev"
|
||||
},
|
||||
"version":"0.1.0-a",
|
||||
"category":"Other",
|
||||
"iconclass":"fa fa-adn",
|
||||
"mimes":[".*"],
|
||||
"category":"Library",
|
||||
"iconclass":"bi bi-database",
|
||||
"mimes":["application/vnd.sqlite3"],
|
||||
"dependencies":[],
|
||||
"locale": {}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
<afx-vbox padding="5">
|
||||
<afx-hbox data-height="35">
|
||||
<afx-button data-id = "bt-add-table" iconclass = "bi bi-plus-lg" data-width="content"></afx-button>
|
||||
<afx-button data-id = "bt-rm-table" iconclass = "bi bi-dash-lg" data-width="content"></afx-button>
|
||||
<afx-list-view dropdown = "true" data-id="tbl-list"></afx-list-view>
|
||||
</afx-hbox>
|
||||
|
||||
@ -13,12 +14,13 @@
|
||||
<afx-hbox tabname = "__(Browse data)" iconclass = "bi bi-table">
|
||||
<afx-vbox>
|
||||
<afx-grid-view data-id="tb-browser"></afx-grid-view>
|
||||
<div data-height="5"></div>
|
||||
<afx-hbox data-height="35">
|
||||
<afx-button iconclass_end="bi bi-chevron-double-right" data-id="bt-load-next" data-width="content"></afx-button>
|
||||
<div></div>
|
||||
<afx-button iconclass="bi bi-plus-lg" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-pencil-square" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-trash-fill" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-plus-lg" data-id="btn-add-record" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-pencil-square" data-id="btn-edit-record" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-trash-fill" data-id= "btn-delete-record" data-width="content"></afx-button>
|
||||
</afx-hbox>
|
||||
</afx-vbox>
|
||||
</afx-hbox>
|
||||
|
337
SQLiteDB/main.ts
337
SQLiteDB/main.ts
@ -1,12 +1,29 @@
|
||||
/**
|
||||
* Define missing API in Array interface
|
||||
*
|
||||
* @interface Array
|
||||
* @template T
|
||||
*/
|
||||
interface Array<T> {
|
||||
/**
|
||||
* Check if the array includes an element
|
||||
*
|
||||
* @param {T} element to check
|
||||
* @returns {boolean}
|
||||
* @memberof Array
|
||||
*/
|
||||
includes(el: T):boolean;
|
||||
}
|
||||
|
||||
namespace OS {
|
||||
export namespace application {
|
||||
|
||||
/**
|
||||
*
|
||||
* @class SQLiteDB
|
||||
* @class SQLiteDBBrowser
|
||||
* @extends {BaseApplication}
|
||||
*/
|
||||
export class SQLiteDB extends BaseApplication {
|
||||
export class SQLiteDBBrowser extends BaseApplication {
|
||||
|
||||
private filehandle: API.VFS.BaseFileHandle;
|
||||
private tbl_list: GUI.tag.ListViewTag;
|
||||
@ -17,7 +34,7 @@ namespace OS {
|
||||
private n_records: number;
|
||||
private btn_loadmore: GUI.tag.ButtonTag;
|
||||
constructor(args: AppArgumentsType[]) {
|
||||
super("SQLiteDB", args);
|
||||
super("SQLiteDBBrowser", args);
|
||||
}
|
||||
|
||||
menu() {
|
||||
@ -26,12 +43,12 @@ namespace OS {
|
||||
text: "__(File)",
|
||||
nodes: [
|
||||
{
|
||||
text: "__(New)",
|
||||
text: "__(New database)",
|
||||
dataid: "new",
|
||||
shortcut: 'A-N'
|
||||
},
|
||||
{
|
||||
text: "__(Open)",
|
||||
text: "__(Open database)",
|
||||
dataid: "open",
|
||||
shortcut: 'A-O'
|
||||
},
|
||||
@ -63,7 +80,7 @@ namespace OS {
|
||||
this.tbl_list.data = list;
|
||||
if(list.length > 0)
|
||||
{
|
||||
this.tbl_list.selected = 0;
|
||||
this.tbl_list.selected = list.length - 1;
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -128,32 +145,75 @@ namespace OS {
|
||||
{
|
||||
if(!this.tbl_list.selectedItem)
|
||||
return;
|
||||
const scheme = this.tbl_list.selectedItem.data.handle.info.scheme;
|
||||
if(!scheme)
|
||||
const schema = this.tbl_list.selectedItem.data.handle.info.schema;
|
||||
if(!schema)
|
||||
return;
|
||||
const data = [];
|
||||
for(let k in scheme)
|
||||
for(let k in schema.types)
|
||||
{
|
||||
data.push([
|
||||
{ text: k},
|
||||
{text: scheme[k]}
|
||||
{text: schema.types[k]}
|
||||
])
|
||||
}
|
||||
this.grid_scheme.rows = data;
|
||||
}
|
||||
}
|
||||
(this.find("bt-rm-table") as GUI.tag.ButtonTag).onbtclick = async (e) => {
|
||||
try {
|
||||
if(!this.filehandle)
|
||||
{
|
||||
return this.notify(__("Please open a database file"));
|
||||
}
|
||||
if(this.tbl_list.selectedItem == undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const table = this.tbl_list.selectedItem.data.name;
|
||||
const ret = await this.openDialog("YesNoDialog", {
|
||||
title: __("Confirm delete?"),
|
||||
text: __("Do you realy want to delete table: {0}", table)
|
||||
});
|
||||
|
||||
(this.find("bt-add-table") as GUI.tag.ButtonTag).onbtclick = (e) => {
|
||||
if(!this.filehandle)
|
||||
{
|
||||
return this.notify(__("Please open a database file"));
|
||||
if(ret)
|
||||
{
|
||||
await this.filehandle.remove(table);
|
||||
this.list_tables();
|
||||
}
|
||||
}
|
||||
this.openDialog(new NewTableDialog(), {
|
||||
title: __("Create new table")
|
||||
})
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
catch(e)
|
||||
{
|
||||
this.error(__("Unable to execute action table delete: {0}", e.toString()), e);
|
||||
}
|
||||
|
||||
}
|
||||
(this.find("bt-add-table") as GUI.tag.ButtonTag).onbtclick = async (e) => {
|
||||
try
|
||||
{
|
||||
if(!this.filehandle)
|
||||
{
|
||||
return this.notify(__("Please open a database file"));
|
||||
}
|
||||
const data = await this.openDialog(new NewTableDialog(), {
|
||||
title: __("Create new table")
|
||||
});
|
||||
this.filehandle.cache = data.schema;
|
||||
await this.filehandle.write(data.name);
|
||||
this.list_tables();
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
this.error(__("Unable to create table: {0}", e.toString()), e);
|
||||
}
|
||||
}
|
||||
(this.find("btn-edit-record") as GUI.tag.ButtonTag).onbtclick = async (e) => {
|
||||
this.edit_record();
|
||||
}
|
||||
(this.find("btn-add-record") as GUI.tag.ButtonTag).onbtclick = async (e) => {
|
||||
this.add_record();
|
||||
}
|
||||
(this.find("btn-delete-record") as GUI.tag.ButtonTag).onbtclick = async (e) => {
|
||||
this.remove_record();
|
||||
}
|
||||
this.btn_loadmore.onbtclick = async (e) => {
|
||||
try
|
||||
@ -173,14 +233,14 @@ namespace OS {
|
||||
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}
|
||||
});
|
||||
const headers = handle.info.schema.fields.map((e) => {
|
||||
return {text: e}
|
||||
});
|
||||
this.grid_table.header = headers;
|
||||
this.grid_table.rows = [];
|
||||
const records = await handle.read({fields:["COUNT(*)"]});
|
||||
this.n_records = records[0]["(COUNT(*))"];
|
||||
this.btn_loadmore.text = `0/${this.n_records}`;
|
||||
await this.load_table();
|
||||
this.container.selectedIndex = 1;
|
||||
}
|
||||
@ -189,12 +249,126 @@ namespace OS {
|
||||
this.error(__("Error reading table: {0}", e.toString()),e);
|
||||
}
|
||||
}
|
||||
this.grid_table.oncelldbclick = async (e) => {
|
||||
this.edit_record();
|
||||
}
|
||||
this.openFile();
|
||||
}
|
||||
private async add_record()
|
||||
{
|
||||
try
|
||||
{
|
||||
const table_handle = this.tbl_list.selectedItem;
|
||||
if(!table_handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const file_hd = table_handle.data.handle;
|
||||
const schema = table_handle.data.handle.info.schema;
|
||||
const model = {};
|
||||
for(let k in schema.types)
|
||||
{
|
||||
if(["INTEGER", "REAL", "NUMERIC"].includes(schema.types[k]))
|
||||
{
|
||||
model[k] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
model[k] = "";
|
||||
}
|
||||
}
|
||||
console.log(model);
|
||||
const data = await this.openDialog(new RecordEditDialog(), {
|
||||
title: __("New record"),
|
||||
schema: schema,
|
||||
record: model
|
||||
});
|
||||
file_hd.cache = data;
|
||||
await file_hd.write(undefined);
|
||||
this.n_records += 1;
|
||||
await this.load_table();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
this.error(__("Error edit/view record: {0}", e.toString()), e);
|
||||
}
|
||||
}
|
||||
private async remove_record()
|
||||
{
|
||||
try
|
||||
{
|
||||
const cell = this.grid_table.selectedCell;
|
||||
const row = this.grid_table.selectedRow;
|
||||
const table_handle = this.tbl_list.selectedItem;
|
||||
if(!cell || !table_handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const pk_id = cell.data.record[table_handle.data.handle.info.schema.pk];
|
||||
const ret = await this.openDialog("YesNoDialog", {
|
||||
title: __("Delete record"),
|
||||
text: __("Do you realy want to delete record {0}",pk_id)
|
||||
});
|
||||
if(!ret)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const file_hd = `${table_handle.data.handle.path}@${pk_id}`.asFileHandle();
|
||||
await file_hd.remove();
|
||||
this.n_records--;
|
||||
// remove the target row
|
||||
this.grid_table.delete(row);
|
||||
this.btn_loadmore.text = `${this.grid_table.rows.length}/${this.n_records}`;
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
this.error(__("Error deleting record: {0}", e.toString()),e);
|
||||
}
|
||||
}
|
||||
private async edit_record()
|
||||
{
|
||||
try
|
||||
{
|
||||
const cell = this.grid_table.selectedCell;
|
||||
const row = this.grid_table.selectedRow;
|
||||
const table_handle = this.tbl_list.selectedItem;
|
||||
if(!cell || !table_handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const data = await this.openDialog(new RecordEditDialog(), {
|
||||
title: __("View/edit record"),
|
||||
schema: table_handle.data.handle.info.schema,
|
||||
record: cell.data.record
|
||||
});
|
||||
const pk_id = cell.data.record[table_handle.data.handle.info.schema.pk];
|
||||
const file_hd = `${table_handle.data.handle.path}@${pk_id}`.asFileHandle();
|
||||
file_hd.cache = data;
|
||||
await file_hd.write(undefined);
|
||||
const row_data = [];
|
||||
for(let k of file_hd.info.schema.fields)
|
||||
{
|
||||
let text:string = data[k];
|
||||
if(text.length > 100)
|
||||
{
|
||||
text = text.substring(0,100);
|
||||
}
|
||||
row_data.push({
|
||||
text: text,
|
||||
record: data
|
||||
});
|
||||
}
|
||||
row.data = row_data;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
this.error(__("Error edit/view record: {0}", e.toString()), e);
|
||||
}
|
||||
}
|
||||
|
||||
private async load_table()
|
||||
{
|
||||
if(this.grid_table.rows.length >= this.n_records)
|
||||
if(this.grid_table.rows && this.grid_table.rows.length >= this.n_records)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -202,15 +376,13 @@ namespace OS {
|
||||
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
|
||||
const headers = handle.info.schema.fields.map((e) => {
|
||||
return {text: e}
|
||||
});
|
||||
// read all records
|
||||
const filter = { where: {}, limit: 10}
|
||||
filter.where[`${handle.info.schema.pk}\$gt`] = this.last_max_id;
|
||||
const records = await handle.read(filter);
|
||||
|
||||
if(records && records.length > 0)
|
||||
{
|
||||
@ -234,13 +406,16 @@ namespace OS {
|
||||
}
|
||||
this.grid_table.push(row, false);
|
||||
}
|
||||
(this.grid_table as any).scroll_to_bottom();
|
||||
this.grid_table.scroll_to_bottom();
|
||||
}
|
||||
|
||||
this.btn_loadmore.text = `${this.grid_table.rows.length}/${this.n_records}`;
|
||||
}
|
||||
}
|
||||
|
||||
SQLiteDBBrowser.dependencies = [
|
||||
"pkg://SQLiteDB/libsqlite.js"
|
||||
]
|
||||
|
||||
class NewTableDialog extends GUI.BasicDialog {
|
||||
/**
|
||||
@ -270,7 +445,6 @@ namespace OS {
|
||||
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();
|
||||
$(this.find("wrapper"))
|
||||
$(this.container)
|
||||
.css("overflow-y", "auto");
|
||||
this.addField();
|
||||
@ -281,7 +455,7 @@ namespace OS {
|
||||
{
|
||||
return this.notify(__("Please enter table name"));
|
||||
}
|
||||
|
||||
const tblname = input.value;
|
||||
const inputs = $("input", this.container) as JQuery<HTMLInputElement>;
|
||||
const lists = $("afx-list-view", this.container) as JQuery<GUI.tag.ListViewTag>;
|
||||
if(inputs.length == 0)
|
||||
@ -301,7 +475,7 @@ namespace OS {
|
||||
cdata[key] = lists[i].selectedItem.data.text;
|
||||
}
|
||||
if (this.handle)
|
||||
this.handle(cdata);
|
||||
this.handle({ name: tblname, schema: cdata});
|
||||
this.quit();
|
||||
}
|
||||
}
|
||||
@ -356,7 +530,7 @@ namespace OS {
|
||||
<afx-label text="__(Fields in table:)" data-height="30"></afx-label>
|
||||
<div data-id="container" style="position:relative;"></div>
|
||||
<afx-hbox data-height="35">
|
||||
<afx-button data-id = "btnAdd" iconclass="fa fa-plus" data-width = "35" ></afx-button>
|
||||
<afx-button data-id = "btnAdd" iconclass="fa fa-plus" data-width = "content" ></afx-button>
|
||||
<div style = "text-align: right;">
|
||||
<afx-button data-id = "btnOk" text = "__(Ok)"></afx-button>
|
||||
<afx-button data-id = "btnCancel" text = "__(Cancel)"></afx-button>
|
||||
@ -364,5 +538,90 @@ namespace OS {
|
||||
</afx-hbox>
|
||||
</afx-vbox>
|
||||
</afx-app-window>`;
|
||||
|
||||
class RecordEditDialog extends GUI.BasicDialog
|
||||
{
|
||||
/**
|
||||
* Reference to the form container
|
||||
*
|
||||
* @private
|
||||
* @type {HTMLDivElement}
|
||||
* @memberof RecordEditDialog
|
||||
*/
|
||||
private container: HTMLDivElement;
|
||||
/**
|
||||
* Creates an instance of RecordEditDialog.
|
||||
* @memberof RecordEditDialog
|
||||
*/
|
||||
constructor() {
|
||||
super("RecordEditDialog");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point
|
||||
*
|
||||
* @memberof RecordEditDialog
|
||||
*/
|
||||
main(): void {
|
||||
super.main();
|
||||
this.container = this.find("container") as HTMLDivElement;
|
||||
(this.find("btnCancel") as GUI.tag.ButtonTag).onbtclick = (e) => this.quit();
|
||||
$(this.container)
|
||||
.css("overflow-y", "auto");
|
||||
if(!this.data || !this.data.schema)
|
||||
{
|
||||
throw new Error(__("No data provided for dialog").__());
|
||||
}
|
||||
for(let k in this.data.schema.types)
|
||||
{
|
||||
const input = $("<afx-input>").appendTo(this.container)[0] as GUI.tag.InputTag;
|
||||
input.uify(this.observable);
|
||||
input.label = k;
|
||||
if(k == this.data.schema.pk)
|
||||
{
|
||||
input.disable = true;
|
||||
}
|
||||
if(this.data.schema.types[k] == "TEXT")
|
||||
{
|
||||
input.verbose = true;
|
||||
$(input).css("height", "100px");
|
||||
}
|
||||
if(this.data.record[k] != undefined)
|
||||
{
|
||||
input.value = this.data.record[k];
|
||||
}
|
||||
}
|
||||
(this.find("btnOk") as GUI.tag.ButtonTag).onbtclick = (e) => {
|
||||
const inputs = $("afx-input", this.container) as JQuery<GUI.tag.InputTag>;
|
||||
const data = {};
|
||||
for(let input of inputs)
|
||||
{
|
||||
data[input.label.__()] = input.value;
|
||||
}
|
||||
if (this.handle)
|
||||
this.handle(data);
|
||||
this.quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheme definition
|
||||
*/
|
||||
RecordEditDialog.scheme = `\
|
||||
<afx-app-window width='550' height='500'>
|
||||
<afx-vbox padding = "5">
|
||||
<div data-id="container" style="row-gap: 5px;"></div>
|
||||
<afx-hbox data-height="35">
|
||||
<div></div>
|
||||
<div data-width="content">
|
||||
<afx-button data-id = "btnOk" text = "__(Ok)"></afx-button>
|
||||
<afx-button data-id = "btnCancel" text = "__(Cancel)"></afx-button>
|
||||
</div>
|
||||
</afx-hbox>
|
||||
</afx-vbox>
|
||||
</afx-app-window>`;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
{
|
||||
"pkgname": "SQLiteDB",
|
||||
"app":"SQLiteDB",
|
||||
"name":"SQLite3 VFS API",
|
||||
"app":"SQLiteDBBrowser",
|
||||
"name":"SQLite3 Browser",
|
||||
"description":"API for manipulate SQLite database as VFS handle",
|
||||
"info":{
|
||||
"author": "",
|
||||
"email": ""
|
||||
"author": "Dany LE",
|
||||
"email": "mrsang@iohub.dev"
|
||||
},
|
||||
"version":"0.1.0-a",
|
||||
"category":"Other",
|
||||
"iconclass":"fa fa-adn",
|
||||
"mimes":[".*"],
|
||||
"category":"Library",
|
||||
"iconclass":"bi bi-database",
|
||||
"mimes":["application/vnd.sqlite3"],
|
||||
"dependencies":[],
|
||||
"locale": {}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
<afx-vbox padding="5">
|
||||
<afx-hbox data-height="35">
|
||||
<afx-button data-id = "bt-add-table" iconclass = "bi bi-plus-lg" data-width="content"></afx-button>
|
||||
<afx-button data-id = "bt-rm-table" iconclass = "bi bi-dash-lg" data-width="content"></afx-button>
|
||||
<afx-list-view dropdown = "true" data-id="tbl-list"></afx-list-view>
|
||||
</afx-hbox>
|
||||
|
||||
@ -13,12 +14,13 @@
|
||||
<afx-hbox tabname = "__(Browse data)" iconclass = "bi bi-table">
|
||||
<afx-vbox>
|
||||
<afx-grid-view data-id="tb-browser"></afx-grid-view>
|
||||
<div data-height="5"></div>
|
||||
<afx-hbox data-height="35">
|
||||
<afx-button iconclass_end="bi bi-chevron-double-right" data-id="bt-load-next" data-width="content"></afx-button>
|
||||
<div></div>
|
||||
<afx-button iconclass="bi bi-plus-lg" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-pencil-square" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-trash-fill" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-plus-lg" data-id="btn-add-record" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-pencil-square" data-id="btn-edit-record" data-width="content"></afx-button>
|
||||
<afx-button iconclass="bi bi-trash-fill" data-id= "btn-delete-record" data-width="content"></afx-button>
|
||||
</afx-hbox>
|
||||
</afx-vbox>
|
||||
</afx-hbox>
|
||||
|
Loading…
Reference in New Issue
Block a user