0
1
Fork 0

Checkpoint

This commit is contained in:
Juan Ferrer 2022-06-06 10:53:59 +02:00
parent de19063ed9
commit 87a583794a
97 changed files with 1436 additions and 1673 deletions

View File

@ -1,4 +1,4 @@
Addresses: Direccions
Addresses: Adreces
Return: Tornar
AddAddress: Afegir adreça
SetAsDefault: Establir com per defecte

View File

@ -31,7 +31,7 @@ Hedera.Catalog = new Class({
else
this.setView(Hedera.Catalog.View.GRID);
this.onRealmChange();
this.onFilterChange();
}
,deactivate: function() {
@ -40,58 +40,81 @@ Hedera.Catalog = new Class({
Vn.Node.remove(this.$.rightPanel);
}
,onFilterChange: function() {
const $ = this.$;
const hash = this.hash;
,getFilter(params, tags, currentTag) {
if (params.search == null && params.type == null)
return null;
if (hash.search != null || hash.type != null) {
const $ = this.$;
params = Object.assign({}, params);
const filter = new Sql.Operation({
type: Sql.Operation.Type.AND
});
let idSearch = false;
if (hash.search != null) {
idSearch = /^\d+$/.test(hash.search);
filter.push(idSearch ? $.idOp : $.nameOp);
if (params.search != null) {
idSearch = /^\d+$/.test(params.search);
if (!idSearch) {
filter.push($.searchOp);
params.search = `%${params.search}%`;
} else
filter.push($.idOp);
}
if (!idSearch) {
if (hash.realm != null)
if (params.realm != null)
filter.push($.realmOp);
if (hash.type != null)
if (params.type != null)
filter.push($.typeOp);
for (const tag of tags)
if (tag != currentTag && params[tag] != null)
filter.push($[`${tag}Op`]);
}
params.filter = filter;
const lot = new Vn.Lot();
lot.setAll(params);
return lot;
}
,onFilterChange: function() {
const $ = this.$;
const params = $.params.$;
this.refreshTitle();
const hasRealm = params.realm != null;
$.filters.style.display = hasRealm ? 'block' : 'none';
$.realmMsg.style.display = hasRealm ? 'none' : 'block';
const tags = [
'color',
'origin',
'category',
'producer'
];
for (const tag of tags)
if (hash[tag] != null)
filter.push($[`${tag}Op`]);
}
const lot = new Vn.Lot();
lot.set('filter', filter);
const lot = this.getFilter(params, tags);
if (lot) {
$.items.lot = lot;
$.items.refresh();
} else
$.items.lot = null;
}
$.items.clean();
,onRealmChange: function() {
const hasRealm = this.$.realm.value != null;
this.$.filters.style.display = hasRealm ? 'block' : 'none';
this.$.realmMsg.style.display = hasRealm ? 'none' : 'block';
this.refreshTitle();
for (const tag of tags)
if (params[tag] != null)
$[`${tag}`].lot = this.getFilter(params, tags);
}
,refreshTitle: function() {
const hash = this.hash.$;
const types = this.$.types;
const realms = this.$.realms;
const type = this.$.type.value;
const realm = this.$.realm.value;
const type = hash.type;
const realm = hash.realm;
let typeName;
let realmName;
@ -232,7 +255,7 @@ Hedera.Catalog = new Class({
this.onEraseClick();
this.$.card.row = form.row;
this.$.cardItem.value = form.$.id;
this.$.cardLot.value = form.$.id;
this.$.cardPopup.show(event.currentTarget);
}
@ -270,7 +293,7 @@ Hedera.Catalog = new Class({
const params = {
warehouse: warehouse,
item: this.$.cardItem.value,
item: this.$.cardLot.value,
amount: amount
}
sql += query.render(params);
@ -295,7 +318,7 @@ Hedera.Catalog = new Class({
,onPopupClose: function() {
this.onEraseClick();
this.$.card.row = -1;
this.$.cardItem.value = undefined;
this.$.cardLot.value = undefined;
}
,onCardLoad: function() {
@ -309,163 +332,3 @@ Hedera.Catalog.extend({
GRID: 1
}
});
Vn.Filter = new Class({
Extends: Htk.Field
,Tag: 'vn-filter'
,Child: 'model'
,Properties: {
model: {
type: Db.Model
,set: function(x) {
x.lot = this._lot;
this._model = x;
this._select.model = x;
}
,get: function() {
return this._model;
}
},
placeholder: {
type: String
,set: function(x) {
this._select.placeholder = x;
this._placeholder = x;
}
,get: function() {
return this._placeholder;
}
},
filter: {
type: Sql.Filter
,set: function(x) {
this._filter = x;
this._lot.set('filter', x);
}
,get: function() {
return this._filter;
}
}
}
,_valueColumnIndex: 0
,_showColumnIndex: 1
,initialize: function(props) {
var node = this.createRoot('div');
node.className = 'vn-filter';
this._select = new Htk.Select();
this._select.on('mousedown', this._onMouseDown, this);
this._select.on('changed', this._onChange, this);
this._select.on('ready', this._onReady, this);
this.node.appendChild(this._select.node);
this._ul = this.createElement('ul');
this.node.appendChild(this._ul);
this._lot = new Vn.Lot();
this.parent(props);
}
,_onMouseDown: function() {
if (this._model && this._model.status === Db.Model.Status.CLEAN)
this._model.refresh();
}
,_onCloseClick: function() {
this._removeSelectionNode();
this._changeValue(undefined);
}
,_removeSelectionNode: function() {
if (this._lastLi) {
Vn.Node.remove(this._lastLi);
this._lastLi = null;
this._label = null;
}
}
,_onChange: function() {
if (this._select.value === null
|| this._select.value === undefined)
return;
this._realSetValue(this._select.value);
}
,_onReady: function() {
if (this._emptyLabel)
this._refreshLabel();
}
,_changeValue: function(newValue) {
this._lot.block();
this.value = newValue;
this._lot.unblock();
}
,_onTimeout: function() {
this._select.value = null;
}
,putValue: function(value) {
this._onMouseDown();
this._realSetValue(value);
}
,_realSetValue: function(value) {
this._removeSelectionNode();
if (value === null || value === undefined)
return;
var li = this._lastLi = this.createElement('li');
this._ul.appendChild(li);
var button = this.createElement('button');
button.addEventListener('click',
this._onCloseClick.bind(this, li));
li.appendChild(button);
var icon = new Htk.Icon({
name: 'close',
alt: _('Close')
});
button.appendChild(icon.node);
var text = this._label = this.createTextNode('');
li.appendChild(text);
setTimeout(this._onTimeout.bind(this));
this._changeValue(value);
this._refreshLabel();
}
,_refreshLabel: function() {
if (!this._label)
return;
let row = -1;
const model = this._model;
let showValue = '';
this._emptyLabel = true;
if (model) {
if (model.ready) {
row = model.searchByIndex(this._valueColumnIndex, this._value);
if (row != -1) {
var label = model.getByIndex(row, this._showColumnIndex);
showValue = label;
this._emptyLabel = false;
}
} else
showValue = _('Loading...');
}
this._label.nodeValue = showValue;
}
});

View File

@ -88,41 +88,7 @@
padding: 0;
width: 100%;
}
.right-panel .vn-filter,
.right-panel select {
margin: 0 auto;
margin-bottom: .7em;
display: block;
}
.vn-filter > ul {
margin: 0;
list-style-type: none;
text-align: left;
color: #666;
padding-left: .8em;
}
.vn-filter li {
margin: 0;
margin-top: .3em;
line-height: 2em;
max-width: 90%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.vn-filter li > button {
vertical-align: middle;
text-align: center;
padding: .2em;
margin: 0;
margin-right: .2em;
}
.vn-filter li > button > span {
display: block;
}
.right-panel .filters > button {
display: block;
margin: 0 auto;
margin-top: 1em;
}

View File

@ -4,8 +4,7 @@
<div id="subtitle"></div>
</div>
<div id="actions" class="catalog-actions">
<htk-search-entry
param="search"/>
<htk-search-entry form="params" column="search"/>
<htk-bar-button
id="view-button"
tip="_Switch view"
@ -27,56 +26,65 @@
<vn-spec name="realm" type="Number"/>
<vn-spec name="type" type="Number"/>
</vn-lot-query>
<vn-lot id="filter-lot"/>
</vn-group>
<vn-group>
<sql-operation
<sql-filter-item
id="id-op"
type="EQUAL">
<sql-field target="i" name="id"/>
<sql-value param="search"/>
</sql-operation>
type="EQUAL"
target="i"
field="id"
param="search"/>
<sql-operation
id="name-op"
type="LIKE">
<sql-field target="i" name="longName"/>
<sql-search-tags param="search"/>
id="search-op"
type="OR">
<sql-filter-item
type="LIKE"
target="i"
field="longName"
param="search"/>
<sql-filter-item
type="LIKE"
target="i"
field="subname"
param="search"/>
</sql-operation>
<sql-operation
<sql-filter-item
id="realm-op"
type="EQUAL">
<sql-field target="t" name="categoryFk"/>
<sql-value param="realm"/>
</sql-operation>
<sql-operation
type="EQUAL"
target="t"
field="categoryFk"
param="realm"/>
<sql-filter-item
id="type-op"
type="EQUAL">
<sql-field target="i" name="typeFk"/>
<sql-value param="type"/>
</sql-operation>
<sql-operation
type="EQUAL"
target="i"
field="typeFk"
param="type"/>
<sql-filter-item
id="color-op"
type="EQUAL">
<sql-field target="i" name="inkFk"/>
<sql-value param="color"/>
</sql-operation>
<sql-operation
type="EQUAL"
id="origin-op">
<sql-field target="i" name="originFk"/>
<sql-value param="origin"/>
</sql-operation>
<sql-operation
target="i"
field="inkFk"
param="color"/>
<sql-filter-item
type="EQUAL"
id="category-op">
<sql-field target="i" name="category"/>
<sql-value param="category"/>
</sql-operation>
<sql-operation
id="origin-op"
target="i"
field="originFk"
param="origin"/>
<sql-filter-item
type="EQUAL"
id="category-op"
target="i"
field="category"
param="category"/>
<sql-filter-item
id="producer-op"
type="EQUAL">
<sql-field target="i" name="producerFk"/>
<sql-value param="producer"/>
</sql-operation>
type="EQUAL"
target="i"
field="producerFk"
param="producer"/>
</vn-group>
<vn-group>
<db-form id="basket" on-ready="onBasketReady">
@ -89,6 +97,7 @@
</db-form>
<db-model
id="items"
auto-load="false"
result-index="2"
on-status-changed="onItemsChange">
CREATE TEMPORARY TABLE tmp.item
@ -230,14 +239,16 @@
</div>
<div id="filters" class="filters">
<h2><t>Filter by</t></h2>
<vn-filter
<htk-combo
placeholder="_Family"
param="type"
form="params"
column="type"
id="type-filter">
<db-model
id="types"
property="model"
conn="conn"
lot="params"
result-index="1"
on-status-changed="refreshTitle">
CALL myBasket_getAvailable;
@ -246,21 +257,21 @@
JOIN vn.itemType t ON t.id = i.typeFk
JOIN tmp.itemAvailable a ON a.id = i.id
JOIN vn.itemTypeL10n l ON l.id = t.id
WHERE t.`order` >= 0 AND #filter
WHERE t.`order` >= 0
AND t.categoryFk = #realm
ORDER BY t.`order`, l.name
</db-model>
<sql-filter property="filter" type="AND">
<sql-filter-item type="EQUAL">
<sql-field name="categoryFk" target="t"/>
<sql-value param="realm"/>
</sql-filter-item>
</sql-filter>
</vn-filter>
<vn-filter
</htk-combo>
<htk-combo
placeholder="_Color"
param="color"
id="color-filter">
<db-model property="model" auto-load="false" result-index="1">
form="params"
column="color"
on-click="$.colors.refresh()">
<db-model
id="colors"
property="model"
auto-load="false"
result-index="1">
CALL myBasket_getAvailable;
SELECT DISTINCT l.id, l.name
FROM vn.item i
@ -270,12 +281,17 @@
WHERE #filter
ORDER BY name
</db-model>
</vn-filter>
<vn-filter
</htk-combo>
<htk-combo
placeholder="_Producer"
param="producer"
id="producer-filter">
<db-model property="model" auto-load="false" result-index="1">
form="params"
column="producer"
on-click="$.producers.refresh()">
<db-model
id="producers"
property="model"
auto-load="false"
result-index="1">
CALL myBasket_getAvailable;
SELECT DISTINCT p.id, p.name
FROM vn.item i
@ -285,12 +301,17 @@
WHERE #filter
ORDER BY name
</db-model>
</vn-filter>
<vn-filter
</htk-combo>
<htk-combo
placeholder="_Origin"
param="origin"
id="origin-filter">
<db-model property="model" auto-load="false" result-index="1">
form="params"
column="origin"
on-click="$.origins.refresh()">
<db-model
id="origins"
property="model"
auto-load="false"
result-index="1">
CALL myBasket_getAvailable;
SELECT DISTINCT o.id, l.name, o.code
FROM vn.item i
@ -301,12 +322,17 @@
WHERE #filter
ORDER BY name
</db-model>
</vn-filter>
<vn-filter
</htk-combo>
<htk-combo
placeholder="_Category"
param="category"
id="category-filter">
<db-model property="model" auto-load="false" result-index="1">
form="params"
column="category"
on-click="$.categorys.refresh()">
<db-model
id="categorys"
property="model"
auto-load="false"
result-index="1">
CALL myBasket_getAvailable;
SELECT DISTINCT i.category, i.category
FROM vn.item i
@ -315,7 +341,7 @@
WHERE #filter
ORDER BY category
</db-model>
</vn-filter>
</htk-combo>
</div>
<div id="order" class="order">
<h2><t>Order by</t></h2>

View File

@ -78,7 +78,7 @@ Model.implement({
lot: {
type: Vn.LotIface
,set: function(x) {
this.link({_lot: x}, {'change': this._autoLoad});
this.link({_lot: x}, {'change': this._onLotChange});
this._onLotChange();
}
,get: function() {
@ -232,25 +232,55 @@ Model.implement({
this.query = query;
}
,_getLotParams: function() {
if (!this._stmt)
return null;
var holders = this._stmt.findHolders();
if (!holders)
return null;
var lotParams = this._lot ? this._lot.params : {};
,_getHolders(stmt) {
if (!stmt) return null;
let holders = this._stmt.findHolders();
if (!holders) return null;
if (this._lot) {
const params = this._lot.params;
for (const holder of holders)
if (params[holder] instanceof Sql.Object) {
const paramHolders = params[holder].findHolders();
if (paramHolders)
holders = holders.concat(paramHolders);
}
}
return holders;
}
,_getHolderValues: function() {
let holders = this._getHolders(this._stmt);
if (!holders) return null;
const lotParams = this._lot ? this._lot.params : {};
const params = {};
for (const holder of holders)
if (!(lotParams[holder] instanceof Sql.Object))
params[holder] = lotParams[holder];
return params;
}
,_getHolderParams: function() {
let holders = this._getHolders(this._stmt);
if (!holders) return null;
const lotParams = this._lot ? this._lot.params : {};
const params = {};
for (const holder of holders)
params[holder] = lotParams[holder];
if (lotParams == null)
lotParams = {};
var params = {};
for (var i = 0; i < holders.length; i++)
params[holders[i]] = lotParams[holders[i]];
return params;
}
,_onLotChange: function() {
var lotParams = this._getLotParams();
if (!Vn.Value.equals(lotParams, this._lastLotParams))
const params = this._getHolderValues();
const isEqual = Vn.Value.equals(params, this._lastParams);
this._lastParams = params;
if (!isEqual)
this._autoLoad();
}
@ -265,12 +295,8 @@ Model.implement({
if (!this._stmt || !this._conn)
return false;
var holders = this._stmt.findHolders();
if (!holders)
return true;
for (var i = 0; i < holders.length; i++)
if (params[holders[i]] === undefined)
for (const param in params)
if (params[param] === undefined)
return false;
return true;
@ -279,22 +305,13 @@ Model.implement({
/**
* Refresh the model data reexecuting the query on the database.
*/
,refresh: function(params) {
var lotParams = this._getLotParams();
var myParams = {};
,refresh: function() {
const params = this._getHolderParams();
Object.assign(myParams, lotParams);
Object.assign(myParams, params);
if (this._filter && (!params || params.filter === undefined))
myParams.filter = this._filter;
this._lastLotParams = lotParams;
if (this._isReady(myParams)) {
if (this._isReady(params)) {
this._setStatus(Status.LOADING);
this._conn.execStmt(this._stmt,
this._selectDone.bind(this), myParams);
this._selectDone.bind(this), params);
} else
this.clean();
}

View File

@ -61,4 +61,4 @@ Training: Formació
Agencies: Agències
Configuration: Configuració
Account: Compte
Addresses: Direccions
Addresses: Adreces

View File

@ -1,6 +1,6 @@
var Widget = require('./widget');
var Assistant = require('./assistant');
require('./style.scss');
var Widget = require('../widget');
var Assistant = require('../assistant');
module.exports = new Class({
Extends: Widget,

View File

@ -0,0 +1,66 @@
.htk-assistant-bar {
position: relative;
padding: .8em;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
width: 100%;
& > button {
border-radius: 50%;
padding: .5em;
margin: 0;
text-align: center;
& > img {
display: block;
width: 1.8em;
padding: .5em;
}
}
& > .end {
display: none;
color: #8cc63f;
& > .icon {
font-size: 1.6rem;
}
}
& > .steps {
display: flex;
align-items: center;
& > div {
background-color: #AAA;
width: .5em;
height: .5em;
cursor: pointer;
border-radius: 50%;
margin: .5em;
transition-property: width, height;
transition-duration: 100ms;
transition-timing-function: ease-in-out;
&.selected {
background-color: #666;
width: 1em;
height: 1em;
}
&:hover {
opacity: .7;
}
}
& > img {
width: 1.3em;
margin: 0 .2em;
cursor: pointer;
&:hover {
opacity: .7;
}
}
}
}

View File

@ -1,7 +1,7 @@
var Widget = require('./widget');
var Step = require('./step');
var Toast = require('./toast');
require('./style.scss');
var Widget = require('../widget');
var Step = require('../step');
var Toast = require('../toast');
/**
* A simple assistant with steps.

View File

@ -0,0 +1,14 @@
.htk-assistant > div {
display: none;
& > h2 {
text-align: center;
font-weight: normal;
font-size: 1.5rem;
margin: 0;
padding: 0;
margin-bottom: 1em;
font-weight: bold;
}
}

View File

@ -1,34 +0,0 @@
module.exports = new Class
({
Extends: Htk.Column
,Tag: 'htk-column-check'
,initialize: function (props)
{
this._cssClass = 'cell-check';
this.parent (props);
}
,render: function (tr)
{
var checkButton = this.createElement ('input');
checkButton.type = 'checkbox';
checkButton.checked = this.value;
if (this.editable)
checkButton.addEventListener ('changed',
this.inputChanged.bind (this, tr, node));
else
checkButton.disabled = true;
var td = this.parent (tr);
td.appendChild (checkButton);
return td;
}
,inputChanged: function (tr, node)
{
this.changed (tr, node.value);
}
});

View File

@ -1,5 +1,5 @@
var NodeBuilder = require('../vn/node-builder');
var NodeBuilder = require('../../vn/node-builder');
/**
* Represents a grid column. This is an abstract class and should not be

View File

@ -1,39 +0,0 @@
module.exports = new Class
({
Extends: Htk.Column
,Tag: 'htk-column-radio'
,Properties:
{
param:
{
type: Vn.Param
,set: function (x)
{
this.radioGroup.master = x;
}
}
}
,initialize: function (props)
{
this._cssClass = 'cell-radio';
this.radioGroup = new Htk.RadioGroup (this.doc);
this.parent (props);
}
,render: function (tr)
{
var td = this.parent (tr);
var radio = this.radioGroup.createButton (this.value);
td.appendChild (radio);
return td;
}
,clear: function ()
{
this.radioGroup.clear ();
}
});

View File

@ -0,0 +1,30 @@
module.exports = new Class({
Extends: Htk.Column
,Tag: 'htk-column-check'
,initialize: function(props) {
this._cssClass = 'cell-check';
this.parent(props);
}
,render: function(tr) {
var checkButton = this.createElement('input');
checkButton.type = 'checkbox';
checkButton.checked = this.value;
if (this.editable)
checkButton.addEventListener('changed',
this.inputChanged.bind(this, tr, node));
else
checkButton.disabled = true;
var td = this.parent(tr);
td.appendChild(checkButton);
return td;
}
,inputChanged: function(tr, node) {
this.changed(tr, node.value);
}
});

View File

@ -1,39 +1,33 @@
module.exports = new Class
({
module.exports = new Class({
Extends: Htk.Column
,Tag: 'htk-column-image'
,Properties:
{
,Properties: {
/**
* The directory where the images are allocated.
*/
directory:
{
directory:{
type: String
,value: null
},
/**
* The subdirectory where the images are allocated.
*/
subdir:
{
subdir:{
type: String
,value: null
},
/**
* Subdirectory where full images are allocated.
*/
fullDir:
{
fullDir:{
type: String
,value: null
},
/**
* The REST connection used to upload the image.
*/
conn:
{
conn:{
type: Vn.JsonConnection
}
}

View File

@ -1,23 +1,19 @@
module.exports = new Class
({
module.exports = new Class({
Extends: Htk.Column
,Tag: 'htk-column-link'
,Properties:
{
,Properties: {
/**
* The link url.
*/
href:
{
href:{
type: String
,value: null
},
/**
* the target where the link is opened.
*/
target:
{
target:{
type: String
,value: null
}

View File

@ -0,0 +1,32 @@
module.exports = new Class({
Extends: Htk.Column
,Tag: 'htk-column-radio'
,Properties: {
param:{
type: Vn.Param
,set: function(x) {
this.radioGroup.master = x;
}
}
}
,initialize: function(props) {
this._cssClass = 'cell-radio';
this.radioGroup = new Htk.RadioGroup(this.doc);
this.parent(props);
}
,render: function(tr) {
var td = this.parent(tr);
var radio = this.radioGroup.createButton(this.value);
td.appendChild(radio);
return td;
}
,clear: function() {
this.radioGroup.clear();
}
});

View File

@ -1,15 +1,12 @@
module.exports = new Class
({
module.exports = new Class({
Extends: Htk.Column
,Tag: 'htk-column-spin'
,Properties:
{
,Properties: {
/**
* The text to append to the number.
*/
unit:
{
unit:{
type: String
,value: null
},

View File

@ -1,15 +1,12 @@
module.exports = new Class
({
module.exports = new Class({
Extends: Htk.Column
,Tag: 'htk-column-text'
,Properties:
{
,Properties: {
/**
* Format that applies to the value.
*/
format:
{
format:{
type: String
,set: function(x) {
this._format = _(x);

View File

@ -1,5 +1,5 @@
const Widget = require('./widget');
const Widget = require('../widget');
module.exports = new Class({
Extends: Widget

View File

@ -1,5 +1,5 @@
var Popup = require('./popup');
require('./style.scss');
var Popup = require('../popup');
/**
* Class to show message dialogs with buttons.

25
js/htk/dialog/style.scss Normal file
View File

@ -0,0 +1,25 @@
.htk-dialog {
padding: 1.5em;
max-width: 20em;
font-weight: normal;
color: #555;
p {
margin: 0;
}
img {
float: left;
height: 3em;
margin-top: 0;
margin-right: 1em;
}
p {
padding: 0;
}
.button-bar > button {
float: right;
margin-left: 1em;
margin-top: .5em;
}
}

View File

@ -1,5 +1,5 @@
var Widget = require('./widget');
var Widget = require('../widget');
module.exports = new Class({
Extends: Widget
@ -109,6 +109,10 @@ module.exports = new Class({
if (this.conditionalFunc)
this.conditionalFunc(this, newValue);
const node = this.node;
if (node && node.nodeType === Node.ELEMENT_NODE)
node.classList.toggle('filled', newValue !== undefined);
this._notifyChanges();
}

View File

@ -1,46 +0,0 @@
module.exports = new Class
({
Extends: Htk.Field
,Tag: 'htk-spin'
,render: function ()
{
var input = this.createRoot ('input');
//setInputTypeNumber (input);
this.node.type = 'number';
input.addEventListener ('change', this._onChange.bind (this));
this.unit = null;
this.digits = 0;
}
,_onChange: function ()
{
var newValue = (this.node.value == '') ? null : parseFloat (this.node.value);
this.node.value = newValue;
this.valueChanged (newValue);
}
,putValue: function (value)
{
var text;
if (value != null)
{
text = (new Number (value)).toFixed (this.digits);
if (this.unit != null)
text += ' ' + this.unit;
}
else
text = '';
this.node.value = text;
}
,setEditable: function (editable)
{
this.node.readOnly = !editable;
}
});

View File

@ -1,5 +1,5 @@
var Button = require('./button');
var Button = require('../button');
module.exports = new Class({
Extends: Button

View File

@ -1,3 +1,4 @@
require('./style.scss');
module.exports = new Class({
Extends: Htk.Field

View File

@ -0,0 +1,10 @@
.htk-button {
display: flex;
align-items: center;
gap: 8px;
& > .htk-icon {
display: block;
}
}

View File

@ -1,3 +1,4 @@
require('./style.scss');
module.exports = new Class({
Extends: Htk.Field

View File

@ -0,0 +1,94 @@
@import "../../style/variables";
@import "../../style/classes";
.htk-calendar {
@extend %box;
width: 20em;
table {
border-collapse: collapse;
}
thead tr,
tfoot tr {
font-weight: normal;
vertical-align: middle;
text-align: center;
height: 3em;
}
thead > tr {
&.weekdays > th {
font-weight: normal;
color: #999;
text-transform: lowercase;
}
& > th {
&.previous, &.next {
font-size: .8rem;
button {
border-radius: 50%;
padding: 10px;
display: block;
margin: 0 auto;
& > .htk-icon {
font-size: 1rem;
}
}
}
&.month-year {
font-size: 1.2rem;
text-transform: lowercase;
}
}
}
tfoot tr {
border-top: none;
}
th.button {
display: table-cell;
}
th.button:hover {
cursor: pointer;
background-color: rgba(1, 1, 1, 0.2);
}
col {
width: 14.2%;
}
tr {
height: 2em;
}
tbody td {
text-align: right;
}
tbody td > div {
height: 2em;
width: 2em;
line-height: 2em;
text-align: center;
border-radius: 2em;
padding: 0.3em;
margin: 0 auto;
color: #555;
}
div {
&.disabled {
color: #bbb;
}
&.today {
font-weight: bold;
color: black;
}
&.selected {
color: white;
background-color: $color-primary;
}
&.enabled {
@extend %clickable;
&:hover {
background-color: rgba(140, 198, 63, 0.8);
}
}
}
}

View File

@ -1,8 +1,7 @@
require('./style.scss');
var Calendar = require('../calendar');
var Calendar = require('./calendar');
module.exports = new Class
({
module.exports = new Class({
Extends: Htk.Field
,Tag: 'htk-date-chooser'

View File

@ -0,0 +1,17 @@
.htk-date-chooser {
display: flex;
align-items: center;
font-weight: normal;
& > span {
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
& > .htk-icon {
flex: none;
color: #666;
}
}

View File

@ -0,0 +1,35 @@
.htk-image {
position: relative;
overflow: hidden;
transition: opacity 250ms ease-out;
&.clickable:hover {
cursor: pointer;
opacity: 0.85;
}
& > img {
display: block;
height: 100%;
width: 100%;
}
& > button {
position: absolute;
top: 0;
left: 0;
padding: 8px;
margin: 4px;
display: none;
background-color: rgba(255, 255, 255, .6);
&:hover {
background-color: rgba(255, 255, 255, .8);
}
}
&:hover > button {
display: block;
}
& > button > .htk-icon {
display: block;
}
}

View File

@ -1,35 +1,30 @@
var htkRadioGroupUid = 0;
module.exports = new Class
({
module.exports = new Class({
Extends: Htk.Field
,Tag: 'htk-radio-group'
,radioLock: false
,initialize: function (props)
{
,initialize: function(props) {
this.clear();
this.parent(props);
}
,clear: function ()
{
,clear: function() {
this.name = htkRadioGroupUid++;
this.buttons = [];
}
,createButton: function (value)
{
,createButton: function(value) {
var button = Vn.Browser.createRadio(this.name, this.doc);
button.value = value;
button.radioGroup = this;
return button;
}
,removeButton: function (button)
{
,removeButton: function(button) {
for (var i = 0; i < this.buttons.length; i++)
if (this.buttons === button) {
this.buttons.splice(i, 1);

View File

@ -1,3 +1,4 @@
require('./style.scss');
module.exports = new Class({
Extends: Htk.Field

View File

@ -0,0 +1,33 @@
.htk-search-entry {
display: flex;
align-items: center;
gap: 6px;
background-color: white;
height: 40px;
border-radius: 20px;
padding: 0 12px;
overflow: hidden;
& > * {
display: inline-block;
vertical-align: middle;
}
& > .htk-icon {
display: block;
margin: 0;
color: gray;
}
& > .entry {
margin: 0;
border: none;
width: 80px;
box-shadow: none;
padding-right: 0;
padding-left: 0;
height: inherit;
}
& > .entry:focus {
background-color: initial;
}
}

View File

@ -1,5 +1,5 @@
var ColumnText = require('../column/text');
require('./style.scss');
var ColumnText = require('../../columns/text');
module.exports = new Class({
Extends: Htk.Field
@ -111,11 +111,21 @@ module.exports = new Class({
const button = this.createRoot('button');
button.type = 'button';
button.className = 'htk-select input';
button.addEventListener('mousedown', this._onButtonMouseDown.bind(this));
button.addEventListener('mousedown',
e => this._onButtonMouseDown(e));
this.label = this.createElement('span');
button.appendChild(this.label);
const erase = new Htk.Icon({
name: 'close',
title: _('Erase')
});
erase.classList.add('erase');
erase.addEventListener('mousedown',
e => this._onEraseMouseDown(e));
button.appendChild(erase.node);
const dropDown = new Htk.Icon({
name: 'expand_more'
});
@ -128,12 +138,22 @@ module.exports = new Class({
this.iterChanged();
}
,_onButtonMouseDown: function(e) {
,_onEraseMouseDown(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this._setRow(-1);
this.valueChanged(undefined);
}
,_onButtonMouseDown: function(event) {
if (this._popup) {
this._popup.hide();
return;
}
if (event.defaultPrevented) return;
event.preventDefault();
var model = this._model;
var menu = this.createElement('div');
@ -151,11 +171,9 @@ module.exports = new Class({
var popup = this._popup = new Htk.Popup({childNode: menu});
popup.on('closed', this._onPopupClose.bind(this));
popup.show(this.node);
popup.show(this.node, event);
this.emit('menu-show');
e.stopPropagation();
}
,_onGridClicked: function(grid, e) {

View File

@ -0,0 +1,53 @@
@import "../../style/variables";
.htk-select {
display: flex;
align-items: center;
font-weight: normal;
width: 100%;
& > span {
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
& > .htk-icon {
flex: none;
color: #666;
&.erase {
display: none
}
}
&:not(.filled) > span {
color: #666;
}
&.filled:hover > .htk-icon.erase {
display: block;
}
}
.htk-select-menu {
height: 100%;
max-height: 80em;
overflow: auto;
min-width: 14em;
tbody > tr {
border-top: none;
height: 2.5em;
}
td.message {
padding: 1em;
}
tr:hover {
background-color: rgba(1, 1, 1, 0.1);
cursor: pointer;
}
td {
max-width: 11em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}

View File

@ -0,0 +1,39 @@
module.exports = new Class({
Extends: Htk.Field
,Tag: 'htk-spin'
,render: function() {
var input = this.createRoot('input');
//setInputTypeNumber (input);
this.node.type = 'number';
input.addEventListener('change', this._onChange.bind(this));
this.unit = null;
this.digits = 0;
}
,_onChange: function() {
var newValue = (this.node.value == '') ? null : parseFloat(this.node.value);
this.node.value = newValue;
this.valueChanged(newValue);
}
,putValue: function(value) {
var text;
if (value != null) {
text = (new Number(value)).toFixed(this.digits);
if (this.unit != null)
text += ' ' + this.unit;
} else
text = '';
this.node.value = text;
}
,setEditable: function(editable) {
this.node.readOnly = !editable;
}
});

View File

@ -1,6 +1,6 @@
var Entry = require('./entry');
var ColumnRadio = require('../column/radio');
var Entry = require('../entry');
var ColumnRadio = require('../../columns/radio');
module.exports = new Class({
Extends: Entry

View File

@ -1,6 +1,6 @@
var Popup = require('./popup');
var Spinner = require('./spinner');
require('./style.scss');
var Popup = require('../popup');
var Spinner = require('../spinner');
module.exports = new Class
({

View File

@ -0,0 +1,12 @@
.htk-full-image {
img {
display: block;
cursor: pointer;
}
.htk-spinner {
background-color: #FFF;
margin: .6em;
display: block;
}
}

View File

@ -1,5 +1,5 @@
var Widget = require('./widget');
require('./style.scss');
var Widget = require('../widget');
module.exports = new Class({
Extends: Widget

63
js/htk/grid/style.scss Normal file
View File

@ -0,0 +1,63 @@
@import "../style/variables";
.htk-grid {
margin: auto;
border-collapse: collapse;
& > thead > tr,
& > tfoot > tr {
background-color: $color-primary;
vertical-align: middle;
height: 3em;
}
th {
color: white;
cursor: pointer;
font-weight: normal;
padding: 0 0.4em;
}
th:hover {
background-color: rgba(1, 1, 1, 0.2);
}
tr {
height: 3.5em;
}
& > tfoot a,
& > thead a {
color: black;
}
tr.pair-row {
background-color: transparent;
}
& > tbody tr {
border-top: 1px solid #DDD;
}
& > tbody tr:first-child {
border-top: none;
}
& > tbody td {
margin: 0;
padding: 0 0.5em;
}
th,
td {
text-align: left;
}
td:first-child,
th:first-child {
padding-left: 1em;
}
td:last-child,
th:last-child {
padding-right: 1em;
}
.message {
padding: 1.5em;
text-align: center;
}
.message > * {
display: inline-block;
vertical-align: middle;
padding-right: .8em;
}
}

View File

@ -18,36 +18,37 @@ Htk = module.exports = {
,AssistantBar : require('./assistant-bar')
,Step : require('./step')
,Loader : require('./loader')
,List : require('./list')
,Field : require('./field')
,Column : require('./column')
};
var Fields = {
Text : require('./field/text')
,Html : require('./field/html')
,Entry : require('./field/entry')
,RadioGroup : require('./field/radio-group')
,Radio : require('./field/radio')
,Label : require('./field/label')
,TextArea : require('./field/text-area')
,Spin : require('./field/spin')
,Check : require('./field/check')
,Select : require('./field/select')
,Calendar : require('./field/calendar')
,DateChooser : require('./field/date-chooser')
,Image : require('./field/image')
,Button : require('./field/button')
,BarButton : require('./field/bar-button')
,Table : require('./field/table')
,SearchEntry : require('./field/search-entry')
,ColumnButton : require('./column/button')
,ColumnLink : require('./column/link')
,ColumnDate : require('./column/date')
,ColumnImage : require('./column/image')
,ColumnRadio : require('./column/radio')
,ColumnSpin : require('./column/spin')
,ColumnText : require('./column/text')
,ColumnCheck : require('./column/check')
Text : require('./fields/text')
,Html : require('./fields/html')
,Entry : require('./fields/entry')
,RadioGroup : require('./fields/radio/radio-group')
,Radio : require('./fields/radio')
,Label : require('./fields/label')
,TextArea : require('./fields/text-area')
,Spin : require('./fields/spin')
,Check : require('./fields/check')
,Select : require('./fields/select')
,Calendar : require('./fields/calendar')
,DateChooser : require('./fields/date-chooser')
,Image : require('./fields/image')
,Button : require('./fields/button')
,BarButton : require('./fields/bar-button')
,Table : require('./fields/table')
,SearchEntry : require('./fields/search-entry')
,ColumnButton : require('./columns/button')
,ColumnLink : require('./columns/link')
,ColumnDate : require('./columns/date')
,ColumnImage : require('./columns/image')
,ColumnRadio : require('./columns/radio')
,ColumnSpin : require('./columns/spin')
,ColumnText : require('./columns/text')
,ColumnCheck : require('./columns/check')
};
for (var field in Fields)

View File

@ -1,5 +1,5 @@
var Widget = require('./widget');
var Widget = require('../widget');
module.exports = new Class({
Extends: Widget

View File

@ -1,7 +1,7 @@
var Component = require('./component');
var Toast = require('./toast');
var Tpl = require('./image-editor.xml').default;
require('./style.scss');
var Component = require('../component');
var Toast = require('../toast');
var Tpl = require('./ui.xml').default;
/**
* A form to handle the image database, it allows to add new images or

View File

@ -0,0 +1,33 @@
.htk-image-editor {
width: 18em;
margin: 0 auto;
padding: 1.5em;
h2 {
color: white;
background-color: #009688;
text-align: left;
font-size: 1.3em;
line-height: 1.7em;
font-weight: normal;
padding: 0.6em 0.8em;
margin: 0;
}
iframe {
display: none;
}
.footer {
margin-top: 2em;
& > .htk-spinner {
padding-right: 1.2em;
height: 1.3em;
width: 1.3em;
}
& > .htk-spinner,
& > input {
float: right;
}
}
}

1
js/htk/list/index.js Normal file
View File

@ -0,0 +1 @@
require('./style.scss');

56
js/htk/list/style.scss Normal file
View File

@ -0,0 +1,56 @@
@import "../style/classes";
.htk-list {
a.item,
.item.clickable {
@extend %clickable;
}
.item {
padding: 20px;
border-bottom: 1px solid #DDD;
display: flex;
align-items: center;
& > .side {
flex: none;
}
& > .content {
flex: 1;
overflow: hidden;
& > .important {
font-weight: bold;
font-size: 1rem;
margin-bottom: .5em;
}
& > p {
margin: .1em 0;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
& > .actions {
flex: none;
display: none;
align-items: center;
& > .htk-button {
margin: 0;
}
& > * {
display: inline-block;
vertical-align: middle;
}
& > input {
margin: .6em;
}
}
}
.item:hover > .actions {
display: flex;
}
.item:last-child {
border-bottom: none;
}
}

View File

@ -1,79 +0,0 @@
var Widget = require ('./widget');
module.exports = new Class
({
Tag: 'htk-loader'
,Extends: Widget
,Properties: {
form:
{
type: Db.Form
,set: function (x)
{
this.link ({_form: x}, {'status-changed': this.onFormChange});
this.onFormChange ();
}
,get: function ()
{
return this._form;
}
}
}
,initialize: function ()
{
var node = this.createRoot ('div');
node.className = 'htk-loader';
var div = this.createElement ('div');
div.className = 'spinner';
var spinner = new Htk.Spinner ();
div.appendChild (spinner.node);
var childs = this.createElement ('div');
this.spinner = spinner;
this.div = div;
this.childs = childs;
this.isLoading = true;
this.stop ();
}
,appendChild: function (child)
{
this.childs.appendChild (child);
}
,stop: function ()
{
if (!this.isLoading)
return;
this.isLoading = false;
this.spinner.stop ();
Vn.Node.removeChilds (this.node);
this.node.appendChild (this.childs);
}
,start: function ()
{
if (this.isLoading)
return;
this.isLoading = true;
this.spinner.start ();
Vn.Node.removeChilds (this.node);
this.node.appendChild (this.div);
}
,onFormChange: function ()
{
if (this._form.ready)
this.stop ();
else
this.start ();
}
});

70
js/htk/loader/index.js Normal file
View File

@ -0,0 +1,70 @@
require('./style.scss');
var Widget = require('../widget');
module.exports = new Class({
Tag: 'htk-loader'
,Extends: Widget
,Properties: {
form: {
type: Db.Form
,set: function(x) {
this.link({_form: x}, {'status-changed': this.onFormChange});
this.onFormChange();
}
,get: function() {
return this._form;
}
}
}
,initialize: function() {
var node = this.createRoot('div');
node.className = 'htk-loader';
var div = this.createElement('div');
div.className = 'spinner';
var spinner = new Htk.Spinner();
div.appendChild(spinner.node);
var childs = this.createElement('div');
this.spinner = spinner;
this.div = div;
this.childs = childs;
this.isLoading = true;
this.stop();
}
,appendChild: function(child) {
this.childs.appendChild(child);
}
,stop: function() {
if (!this.isLoading)
return;
this.isLoading = false;
this.spinner.stop();
Vn.Node.removeChilds(this.node);
this.node.appendChild(this.childs);
}
,start: function() {
if (this.isLoading)
return;
this.isLoading = true;
this.spinner.start();
Vn.Node.removeChilds(this.node);
this.node.appendChild(this.div);
}
,onFormChange: function() {
if (this._form.ready)
this.stop();
else
this.start();
}
});

4
js/htk/loader/style.scss Normal file
View File

@ -0,0 +1,4 @@
.htk-loader > .spinner {
text-align: center;
}

View File

@ -27,3 +27,4 @@ Next: Següent
Confirm: Confirmar
Search: Cercar
Search...: Cercar...
Erase: Esborrar

View File

@ -27,3 +27,4 @@ Next: Next
Confirm: Confirm
Search: Search
Search...: Search...
Erase: Erase

View File

@ -27,3 +27,4 @@ Next: Siguiente
Confirm: Confirmar
Search: Buscar
Search...: Buscar...
Erase: Borrar

View File

@ -27,3 +27,4 @@ Next: Suivant
Confirm: Confirmer
Search: Recherche
Search...: Recherche...
Erase: Effacer

View File

@ -27,3 +27,4 @@ Next: Seguinte
Confirm: Confirmar
Search: Procurar
Search...: Procurar...
Erase: Apagar

View File

@ -1,20 +1,17 @@
var Widget = require('./widget');
require('./style.scss');
var Widget = require('../widget');
/**
* Class to handle popups.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Widget
,Tag: 'htk-popup'
,Properties:
{
,Properties: {
/**
* The popup child.
*/
child:
{
child: {
type: Widget
,set: function(x) {
this._child = x;
@ -27,8 +24,7 @@ module.exports = new Class
/**
* The popup child Node.
*/
,childNode:
{
,childNode: {
type: Object
,set: function(x) {
this._child = null;
@ -41,8 +37,7 @@ module.exports = new Class
/**
* Indicates how the dialog must be displayed.
*/
,modal:
{
,modal: {
type: Boolean
,set: function(x) {
this._modal = x;
@ -73,8 +68,9 @@ module.exports = new Class
this.node.appendChild(childNode);
}
,show: function(parent) {
,show: function(parent, event) {
this._parent = parent;
this._lastEvent = event;
this.open();
}

32
js/htk/popup/style.scss Normal file
View File

@ -0,0 +1,32 @@
.htk-popup {
z-index: 200;
display: block;
position: fixed;
overflow: hidden;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 0.4em rgba(1, 1, 1, 0.6);
box-sizing: content-box;
&.modal {
position: absolute;
font-size: 1.2em;
top: 50%;
left: 50%;
}
& > * {
border-radius: 0.1em;
}
}
.htk-background {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 190;
background-color: rgba(1, 1, 1, 0.7);
opacity: 0;
transition: opacity 200ms ease-in-out;
}

View File

@ -1,5 +1,5 @@
var Widget = require('./widget');
require('./style.scss');
var Widget = require('../widget');
module.exports = new Class({
Extends: Widget

View File

@ -0,0 +1,16 @@
.htk-repeater {
& > .message {
padding: 1.5em;
text-align: center;
& > * {
vertical-align: middle;
}
& > span,
& > .htk-spinner {
display: inline-block;
padding-right: 10px;
}
}
}

View File

@ -1,38 +0,0 @@
var Widget = require ('./widget');
module.exports = new Class
({
Extends: Widget
,Tag: 'htk-spinner'
,_started: false
,render: function ()
{
var loader = this.createRoot ('div');
loader.className = 'htk-spinner';
var spin = this.spin = this.createElement ('div');
loader.appendChild (spin);
}
,start: function ()
{
if (this._started)
return;
Vn.Node.addClass (this.spin, 'spinner');
this._started = true;
}
,stop: function ()
{
if (!this._started)
return;
Vn.Node.removeClass (this.spin, 'spinner');
this._started = false;
}
});

34
js/htk/spinner/index.js Normal file
View File

@ -0,0 +1,34 @@
require('./style.scss');
var Widget = require('../widget');
module.exports = new Class({
Extends: Widget
,Tag: 'htk-spinner'
,_started: false
,render: function() {
var loader = this.createRoot('div');
loader.className = 'htk-spinner';
var spin = this.spin = this.createElement('div');
loader.appendChild(spin);
}
,start: function() {
if (this._started)
return;
Vn.Node.addClass(this.spin, 'spinner');
this._started = true;
}
,stop: function() {
if (!this._started)
return;
Vn.Node.removeClass(this.spin, 'spinner');
this._started = false;
}
});

31
js/htk/spinner/style.scss Normal file
View File

@ -0,0 +1,31 @@
.htk-spinner {
width: 1.8em;
height: 1.8em;
position: relative;
display: inline-block;
& > .spinner {
left: 0;
position: absolute;
width: inherit;
height: inherit;
box-sizing: border-box;
border-radius: 50%;
border: 2px solid transparent;
border-top-color: #666;
border-left-color: #666;
animation: spinner 1s linear infinite;
-webkit-animation: spinner 1s linear infinite;
}
&.dark > .spinner {
border-top-color: white;
border-left-color: white;
}
}
@keyframes spinner {
to {transform: rotate(360deg);}
}
@-webkit-keyframes spinner {
to {-webkit-transform: rotate(360deg);}
}

View File

@ -1,5 +1,5 @@
var Widget = require('./widget');
var Widget = require('../widget');
module.exports = new Class({
Extends: Widget,

View File

@ -1,106 +1,6 @@
@import "./variables";
/* Icon */
.htk-icon {}
/* Button */
.htk-button {
display: flex;
align-items: center;
gap: 8px;
& > .htk-icon {
display: block;
}
}
/* Grid */
.htk-grid {
margin: auto;
border-collapse: collapse;
& > thead > tr,
& > tfoot > tr {
background-color: $color-primary;
vertical-align: middle;
height: 3em;
}
th {
color: white;
cursor: pointer;
font-weight: normal;
padding: 0 0.4em;
}
th:hover {
background-color: rgba(1, 1, 1, 0.2);
}
tr {
height: 3.5em;
}
& > tfoot a,
& > thead a {
color: black;
}
tr.pair-row {
background-color: transparent;
}
& > tbody tr {
border-top: 1px solid #DDD;
}
& > tbody tr:first-child {
border-top: none;
}
& > tbody td {
margin: 0;
padding: 0 0.5em;
}
th,
td {
text-align: left;
}
td:first-child,
th:first-child {
padding-left: 1em;
}
td:last-child,
th:last-child {
padding-right: 1em;
}
.message {
padding: 1.5em;
text-align: center;
}
.message > * {
display: inline-block;
vertical-align: middle;
padding-right: .8em;
}
}
/* Repater */
.htk-repeater {
& > .message {
padding: 1.5em;
text-align: center;
& > * {
vertical-align: middle;
}
& > span,
& > .htk-spinner {
display: inline-block;
padding-right: 10px;
}
}
}
/* Grid cells */
th.cell-spin {
text-align: right;
}
@ -108,16 +8,18 @@ td.cell-spin {
width: 2.5em;
text-align: right;
}
th.cell-check,
th.cell-radio {
text-align: center;
}
td.cell-button {
max-width: 20px;
text-align: center;
}
td.cell-button > button,
td.cell-button > a {
& > button,
& > a {
@extend %clickable;
display: block;
height: 44px;
@ -132,577 +34,26 @@ td.cell-button > a {
& > .htk-icon {
display: block;
}
}
td.cell-button > button:hover,
td.cell-button > a:hover {
&:hover {
background-color: rgba(1, 1, 1, 0.1);
}
td.cell-button img {
}
img {
height: 1.5em;
width: 1.5em;
display: block;
margin: auto;
padding: 0;
}
}
td.cell-image {
text-align: center;
}
td.cell-image .htk-image {
.htk-image {
max-width: 2.5em;
max-height: 2.5em;
display: block;
margin: auto;
}
/* Select */
.htk-select,
.htk-date-chooser {
display: flex;
align-items: center;
font-weight: normal;
& > span {
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
& > .htk-icon {
flex: none;
color: #666;
}
}
.htk-select {
width: 100%;
}
.htk-select-menu {
height: 100%;
max-height: 80em;
overflow: auto;
min-width: 14em;
tbody > tr {
border-top: none;
height: 2.5em;
}
td.message {
padding: 1em;
}
tr:hover {
background-color: rgba(1, 1, 1, 0.1);
cursor: pointer;
}
td {
max-width: 11em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
/* List */
.htk-list {
a.item,
.item.clickable {
@extend %clickable;
}
.item {
padding: 20px;
border-bottom: 1px solid #DDD;
display: flex;
align-items: center;
& > .side {
flex: none;
}
& > .content {
flex: 1;
overflow: hidden;
& > .important {
font-weight: bold;
font-size: 1rem;
margin-bottom: .5em;
}
& > p {
margin: .1em 0;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
& > .actions {
flex: none;
display: none;
align-items: center;
& > .htk-button {
margin: 0;
}
& > * {
display: inline-block;
vertical-align: middle;
}
& > input {
margin: .6em;
}
}
}
.item:hover > .actions {
display: flex;
}
.item:last-child {
border-bottom: none;
}
}
/* Calendar */
.htk-calendar {
@extend %box;
width: 20em;
table {
border-collapse: collapse;
}
thead tr,
tfoot tr {
font-weight: normal;
vertical-align: middle;
text-align: center;
height: 3em;
}
thead > tr {
&.weekdays > th {
font-weight: normal;
color: #999;
text-transform: lowercase;
}
& > th {
&.previous, &.next {
font-size: .8rem;
button {
border-radius: 50%;
padding: 10px;
display: block;
margin: 0 auto;
& > .htk-icon {
font-size: 1rem;
}
}
}
&.month-year {
font-size: 1.2rem;
text-transform: lowercase;
}
}
}
tfoot tr {
border-top: none;
}
th.button {
display: table-cell;
}
th.button:hover {
cursor: pointer;
background-color: rgba(1, 1, 1, 0.2);
}
col {
width: 14.2%;
}
tr {
height: 2em;
}
tbody td {
text-align: right;
}
tbody td > div {
height: 2em;
width: 2em;
line-height: 2em;
text-align: center;
border-radius: 2em;
padding: 0.3em;
margin: 0 auto;
color: #555;
}
div {
&.disabled {
color: #bbb;
}
&.today {
font-weight: bold;
color: black;
}
&.selected {
color: white;
background-color: $color-primary;
}
&.enabled {
@extend %clickable;
&:hover {
background-color: rgba(140, 198, 63, 0.8);
}
}
}
}
/* Image */
.htk-image {
position: relative;
overflow: hidden;
transition: opacity 250ms ease-out;
&.clickable:hover {
cursor: pointer;
opacity: 0.85;
}
& > img {
display: block;
height: 100%;
width: 100%;
}
& > button {
position: absolute;
top: 0;
left: 0;
padding: 8px;
margin: 4px;
display: none;
background-color: rgba(255, 255, 255, .6);
&:hover {
background-color: rgba(255, 255, 255, .8);
}
}
&:hover > button {
display: block;
}
& > button > .htk-icon {
display: block;
}
}
/* Full image */
.htk-full-image {
img {
display: block;
cursor: pointer;
}
.htk-spinner {
background-color: #FFF;
margin: .6em;
display: block;
}
}
/* Image editor */
.htk-image-editor {
width: 18em;
margin: 0 auto;
padding: 1.5em;
h2 {
color: white;
background-color: #009688;
text-align: left;
font-size: 1.3em;
line-height: 1.7em;
font-weight: normal;
padding: 0.6em 0.8em;
margin: 0;
}
iframe {
display: none;
}
.footer {
margin-top: 2em;
& > .htk-spinner {
padding-right: 1.2em;
height: 1.3em;
width: 1.3em;
}
& > .htk-spinner,
& > input {
float: right;
}
}
}
/* Toast */
.htk-toast {
z-index: 210;
display: block;
position: fixed;
left: 50%;
top: 4em;
width: 21em;
margin-left: -10.5em;
text-align: center;
overflow: auto;
max-height: 40em;
overflow: visible;
& > div {
margin: .5em 0;
padding: .5em 2%;
border-radius: 0.1em;
box-shadow: 0 0 0.4em rgba(1, 1, 1, 0.6);
width: 96%;
opacity: 0;
transform: translateZ(0) translateY(-1em);
-webkit-transform: translateZ(0) translateY(-1em);
transition-property: opacity, transform;
transition-duration: 200ms;
transition-timing-function: ease-out;
&.show {
opacity: 1;
transform: translateZ(0) translateY(0em);
-webkit-transform: translateZ(0) translateY(0em);
}
}
& > .message {
background-color: #BBFFBB;
color: #363;
}
& > .warning {
background-color: #FFE0B2;
color: #C30;
}
& > .error {
background-color: #FFCDD2;
color: #A00;
}
}
/* Popup */
.htk-background {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 190;
background-color: rgba(1, 1, 1, 0.7);
opacity: 0;
transition: opacity 200ms ease-in-out;
}
.htk-popup {
z-index: 200;
display: block;
position: fixed;
overflow: hidden;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 0.4em rgba(1, 1, 1, 0.6);
box-sizing: content-box;
&.modal {
position: absolute;
font-size: 1.2em;
top: 50%;
left: 50%;
}
& > * {
border-radius: 0.1em;
}
}
/* Dialog */
.htk-dialog {
padding: 1.5em;
max-width: 20em;
font-weight: normal;
color: #555;
p {
margin: 0;
}
img {
float: left;
height: 3em;
margin-top: 0;
margin-right: 1em;
}
p {
padding: 0;
}
.button-bar > button {
float: right;
margin-left: 1em;
margin-top: .5em;
}
}
/* Assistant */
.htk-assistant > div {
display: none;
& > h2 {
text-align: center;
font-weight: normal;
font-size: 1.5rem;
margin: 0;
padding: 0;
margin-bottom: 1em;
font-weight: bold;
}
}
/* Assistant bar */
.htk-assistant-bar {
position: relative;
padding: .8em;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
width: 100%;
& > button {
border-radius: 50%;
padding: .5em;
margin: 0;
text-align: center;
& > img {
display: block;
width: 1.8em;
padding: .5em;
}
}
& > .end {
display: none;
color: #8cc63f;
& > .icon {
font-size: 1.6rem;
}
}
& > .steps {
display: flex;
align-items: center;
& > div {
background-color: #AAA;
width: .5em;
height: .5em;
cursor: pointer;
border-radius: 50%;
margin: .5em;
transition-property: width, height;
transition-duration: 100ms;
transition-timing-function: ease-in-out;
&.selected {
background-color: #666;
width: 1em;
height: 1em;
}
&:hover {
opacity: .7;
}
}
& > img {
width: 1.3em;
margin: 0 .2em;
cursor: pointer;
&:hover {
opacity: .7;
}
}
}
}
/* Search entry */
.htk-search-entry {
display: flex;
align-items: center;
gap: 6px;
background-color: white;
height: 40px;
border-radius: 20px;
padding: 0 12px;
overflow: hidden;
& > * {
display: inline-block;
vertical-align: middle;
}
& > .htk-icon {
display: block;
margin: 0;
color: gray;
}
& > .entry {
margin: 0;
border: none;
width: 80px;
box-shadow: none;
padding-right: 0;
padding-left: 0;
height: inherit;
}
& > .entry:focus {
background-color: initial;
}
}
/* Spinner */
.htk-spinner {
width: 1.8em;
height: 1.8em;
position: relative;
display: inline-block;
& > .spinner {
left: 0;
position: absolute;
width: inherit;
height: inherit;
box-sizing: border-box;
border-radius: 50%;
border: 2px solid transparent;
border-top-color: #666;
border-left-color: #666;
animation: spinner 1s linear infinite;
-webkit-animation: spinner 1s linear infinite;
}
&.dark > .spinner {
border-top-color: white;
border-left-color: white;
}
}
@keyframes spinner {
to {transform: rotate(360deg);}
}
@-webkit-keyframes spinner {
to {-webkit-transform: rotate(360deg);}
}
/* Loader */
.htk-loader > .spinner {
text-align: center;
}

View File

@ -1,3 +1,5 @@
require('./style.scss');
/**
* Class to show toast messages.
*/

47
js/htk/toast/style.scss Normal file
View File

@ -0,0 +1,47 @@
.htk-toast {
z-index: 210;
display: block;
position: fixed;
left: 50%;
top: 4em;
width: 21em;
margin-left: -10.5em;
text-align: center;
overflow: auto;
max-height: 40em;
overflow: visible;
& > div {
margin: .5em 0;
padding: .5em 2%;
border-radius: 0.1em;
box-shadow: 0 0 0.4em rgba(1, 1, 1, 0.6);
width: 96%;
opacity: 0;
transform: translateZ(0) translateY(-1em);
-webkit-transform: translateZ(0) translateY(-1em);
transition-property: opacity, transform;
transition-duration: 200ms;
transition-timing-function: ease-out;
&.show {
opacity: 1;
transform: translateZ(0) translateY(0em);
-webkit-transform: translateZ(0) translateY(0em);
}
}
& > .message {
background-color: #BBFFBB;
color: #363;
}
& > .warning {
background-color: #FFE0B2;
color: #C30;
}
& > .error {
background-color: #FFCDD2;
color: #A00;
}
}

View File

@ -1,7 +1,7 @@
const NodeBuilder = require('../vn/node-builder');
const NodeBuilder = require('../../vn/node-builder');
const Widget = new Class({
const WidgetClass = {
Extends: NodeBuilder
,Properties: {
/**
@ -115,7 +115,7 @@ const Widget = new Class({
} else
this.parent(id, callback, instance);
}
});
};
htmlEventMap = {};
htmlEvents = [
@ -141,9 +141,9 @@ htmlMethods = [
'addEventListener'
];
htmlMethods.forEach(method => {
Widget[method] = function() {
this.node.apply(this.node, arguments);
WidgetClass[method] = function() {
this.node[method].apply(this.node, arguments);
};
});
module.exports = Widget;
module.exports = new Class(WidgetClass);

View File

@ -6,32 +6,27 @@ var Expr = require ('./expr');
*
* @param {string} taget The name of the owner table
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Expr
,Tag: 'sql-field'
,Properties:
{
,Properties: {
/**
* The column name.
*/
name:
{
name: {
type: String
,value: null
},
/**
* The source table name or its alias if it has been specified.
*/
target:
{
target: {
type: String
,value: null
}
}
,render: function ()
{
,render: function() {
return this.renderPreIdent(this.target)
+ this.renderIdent(this.name);
}

View File

@ -36,7 +36,7 @@ module.exports = new Class({
}
/**
* Checks if parameter name haa been defined and if it has a value.
* Checks if parameter name has been defined and if it has a value.
*/
,isReady: function(params) {
return this.param != null && params != null && params[this.param] != null;
@ -51,18 +51,12 @@ module.exports = new Class({
}));
var value = params[this.param];
if (this.type === Operation.Type.LIKE && typeof value === 'string') {
value = value.replace(/[\%\?]/g, this._escapeChar);
value = value.replace(/^|\s+|$/g, '%');
}
newOp.push(new Value({value: value}));
return newOp.render(params);
}
,_escapeChar: function(char) {
return '\\'+ char;
,findHolders: function() {
return this.param ? [this.param] : null;
}
});

View File

@ -6,16 +6,14 @@ var Value = require ('./value');
* The equivalent of a SQL filter expression. It allows to automatically build
* SQL filters based on lot parameters.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Operation
,Tag: 'sql-filter'
/**
* Checks if any of filters childs are ready.
*/
,isReady: function (params)
{
,isReady: function(params) {
var exprs = this.exprs;
for (var i = exprs.length; i--;)
if (exprs[i].isReady(params))
@ -29,8 +27,7 @@ module.exports = new Class
* ready is ommitted from the expression. If all of its childs aren't ready
* renders the TRUE expression.
*/
,render: function (params)
{
,render: function(params) {
var newOp;
var newExprs = [];

View File

@ -5,48 +5,40 @@ var ListHolder = require ('./list-holder');
/**
* The equivalent of a SQL function.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Expr
,Tag: 'sql-function'
,Implements: ListHolder
,Properties:
{
,Properties: {
/**
* The function name.
*/
name:
{
name: {
type: String
,value: null
},
/**
* The function schema.
*/
schema:
{
schema: {
type: String
,value: null
},
/**
* The function parameters.
*/
params:
{
params: {
type: Array
,set: function (x)
{
,set: function(x) {
this.list = x;
}
,get: function ()
{
,get: function() {
return this.list;
}
}
}
,render: function (params)
{
,render: function(params) {
return this.renderPreIdent(this.schema)
+ this.renderIdent(this.name)
+ '('

View File

@ -5,33 +5,25 @@ var Value = require ('./value');
/**
* A holder for another object.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: SqlObject
,Properties:
{
id:
{
,Properties: {
id: {
type: String
,value: null
}
}
,render: function (params)
{
if (params)
{
,render: function(params) {
if (params) {
var object = params[this.id];
if (object !== undefined)
{
if (!(object instanceof SqlObject))
{
if (object !== undefined) {
if (!(object instanceof SqlObject)) {
var sqlValue = new Value();
sqlValue.value = object;
return sqlValue.render();
}
else
} else
return object.render(params);
}
}

View File

@ -4,12 +4,10 @@ var Dml = require ('./dml');
/**
* The equivalent of a SQL insert.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Dml
,render: function (params)
{
,render: function(params) {
return 'INSERT INTO'
+ this.renderTarget(params)
+ ' ('

View File

@ -13,40 +13,34 @@ var TypeSql = [
/**
* The equivalent of a SQL join.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: SqlObject
,Tag: 'sql-join-table'
,Properties:
{
,Properties: {
/**
* The join type.
*/
type:
{
type: {
enumType: Type
,value: 0
},
/**
* The right target.
*/
target:
{
target: {
type: Target
,value: null
},
/**
* The join on condition.
*/
condition:
{
condition: {
type: Expr
,value: null
}
}
,render: function (params)
{
,render: function(params) {
return TypeSql[this.type] +' JOIN '
+ this.target.render(params)
+ this.renderIfSet(this.condition, 'ON', params);

View File

@ -14,37 +14,30 @@ var Type = {
RIGHT : 2
};
Klass.extend
({
Klass.extend({
Type: Type
});
Klass.implement
({
Klass.implement({
Extends: Target
,Implements: ListHolder
,Tag: 'sql-join'
,Properties:
{
,Properties: {
/**
* The right targets.
*/
targets:
{
targets: {
type: Array
,set: function (x)
{
,set: function(x) {
this.list = x;
}
,get: function ()
{
,get: function() {
return this.list;
}
}
}
,render: function (params)
{
,render: function(params) {
return '('
+ this.target.render(params)
+ ' '

View File

@ -1,20 +1,14 @@
/**
* Interface for array holders.
*/
module.exports = new Class
({
Properties:
{
/*
list:
{
module.exports = new Class({
Properties: {
/* list: {
type: Array
,set: function (x)
{
,set: function (x) {
this._list = x;
}
,get: function ()
{
,get: function () {
return this._list;
}
}
@ -23,8 +17,7 @@ module.exports = new Class
,list: []
,appendChild: function (child)
{
,appendChild: function(child) {
this.list.push(child);
}
@ -33,8 +26,7 @@ module.exports = new Class
*
* @param {SqlObject} element The element to add
*/
,push: function (element)
{
,push: function(element) {
this.list.push(element);
}
@ -43,8 +35,7 @@ module.exports = new Class
*
* @param {Number} i The element index
*/
,splice: function (i)
{
,splice: function(i) {
this.list.splice(i);
}
@ -53,8 +44,18 @@ module.exports = new Class
*
* @param {Number} i The element index
*/
,get: function (i)
{
,get: function(i) {
return this.list[i];
}
,findHolders() {
let ids = [];
for (const object of this.list){
holders = object.findHolders();
if (holders) ids = ids.concat(holders);
}
return ids;
}
});

View File

@ -4,42 +4,35 @@ var Object = require ('./object');
/**
* List of Sql.Object
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Object
,objects: []
,add: function (object)
{
,add: function(object) {
this.objects.push(object.ref());
object.on('changed', this._onObjectChange, this);
this._onObjectChange();
}
,get: function (i)
{
,get: function(i) {
return objects[i];
}
,getArray: function ()
{
,getArray: function() {
return this.objects;
}
,remove: function (i)
{
,remove: function(i) {
this._unrefObject(this.objects.splice(i, 1));
this._onObjectChange();
}
,_onObjectChange: function ()
{
,_onObjectChange: function() {
this.emit('changed');
}
,isReady: function ()
{
,isReady: function() {
var o = this.objects;
if (o.length == 0)
@ -52,14 +45,23 @@ module.exports = new Class
return true;
}
,_unrefObject: function (object)
{
,findHolders() {
let ids = [];
for (const object of this.objects){
holders = object.findHolders();
if (holders) ids = ids.concat(holders);
}
return ids;
}
,_unrefObject: function(object) {
object.disconnect('changed', this._onObjectChange, this);
object.unref();
}
,_destroy: function ()
{
,_destroy: function() {
for (var i = 0; i < this.objects.length; i++)
this._unrefObject(this.objects[i]);

View File

@ -5,32 +5,26 @@ var ListHolder = require ('./list-holder');
/**
* The equivalent of a SQL multi statement.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Stmt
,Implements: ListHolder
,Tag: 'sql-multi-stmt'
,Properties:
{
,Properties: {
/**
* The statements list.
*/
stmts:
{
stmts: {
type: Array
,set: function (x)
{
,set: function(x) {
this.list = x;
}
,get: function ()
{
,get: function() {
return this.list;
}
}
}
,render: function (params)
{
,render: function(params) {
return this.renderListWs(this.list, params, ";\n");
}
});

View File

@ -11,8 +11,7 @@ var ListHolder = require ('./list-holder');
var Klass = new Class();
module.exports = Klass;
var Type =
{
var Type = {
EQUAL : 0
,LIKE : 1
,AND : 2
@ -30,8 +29,7 @@ var Type =
,MOD : 14
};
var Operators =
[
var Operators = [
'='
,'LIKE'
,'AND'
@ -49,45 +47,36 @@ var Operators =
,'MOD'
];
Klass.extend
({
Klass.extend({
Type: Type
,Operators: Operators
});
Klass.implement
({
Klass.implement({
Extends: Expr
,Implements: ListHolder
,Tag: 'sql-operation'
,Properties:
{
type:
{
,Properties: {
type: {
enumType: Type
,value: -1
},
target:
{
target: {
type: String
,value: null
},
exprs:
{
exprs: {
type: Array
,set: function (x)
{
,set: function(x) {
this.list = x;
}
,get: function ()
{
,get: function() {
return this.list;
}
}
}
,render: function (params)
{
,render: function(params) {
var operator = ' '+ Operators[this.type] +' ';
return '('
+ this.renderListWs(this.list, params, operator)

View File

@ -5,19 +5,16 @@ var Field = require ('./field');
/**
* The equivalent of a SQL select.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Stmt
,expr: []
,addField: function (fieldName)
{
,addField: function(fieldName) {
this.expr.push(new Field({name: fieldName}));
}
,render: function (params)
{
,render: function(params) {
return 'SELECT '
+ this.renderListWs(this.expr, params, ', ')
+ ' FROM'

View File

@ -5,14 +5,11 @@ var Holder = require ('./holder');
/**
* Literal SQL string.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Stmt
,Tag: 'sql-string'
,Properties:
{
query:
{
,Properties: {
query: {
type: String
,value: null
}
@ -20,19 +17,16 @@ module.exports = new Class
,regexp: /#\w+/g
,appendChild: function (child)
{
,appendChild: function(child) {
if (child.nodeType === Node.TEXT_NODE)
this.query = child.textContent;
}
,render: function (params)
{
,render: function(params) {
if (!this.query)
return null;
function replaceFunc (token)
{
function replaceFunc(token) {
var holder = new Holder({id: token.substr(1)});
return holder.render(params);
}
@ -40,8 +34,7 @@ module.exports = new Class
return this.query.replace(this.regexp, replaceFunc);
}
,findHolders: function ()
{
,findHolders: function() {
var ids = this.query.match(this.regexp);
if (ids)

View File

@ -4,30 +4,24 @@ var Target = require ('./target');
/**
* Represents a database table.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Target
,Properties:
{
name:
{
,Properties: {
name: {
type: String
,value: null
},
alias:
{
alias: {
type: String
,value: null
},
schema:
{
schema: {
type: String
,value: null
}
}
,render: function ()
{
,render: function() {
var sql = this.renderPreIdent(this.schema)
+ this.renderIdent(this.name);

View File

@ -4,7 +4,6 @@ var Object = require ('./object');
/**
* The equivalent of a SQL target.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Object
});

View File

@ -4,12 +4,10 @@ var Dml = require ('./dml');
/**
* The equivalent of a SQL update.
*/
module.exports = new Class
({
module.exports = new Class({
Extends: Dml
,render: function (params)
{
,render: function(params) {
var sql = 'UPDATE'
+ this.renderTarget(params)
+ ' SET ';