<!-- 
/*
Copyright 2017 apHarmony

This file is part of jsHarmony.

jsHarmony is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

jsHarmony is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this package.  If not, see <http://www.gnu.org/licenses/>.
*/
-->
<script type="text/x-tutorial-info">
{
  "ID": "model_styling",
  "Title": "Model Styling / CSS / Fonts",
  "Menu": ["Models","Models Overview"],
  "Code": [
    "/models/ModelStyle_css.json",
    "/models/ModelStyle_tabpanelstyle.json",
    "/models/ModelStyle_tablestyle.json",
    "/models/ModelStyle_rowstyle.json",
    "/models/ModelStyle_formstyle.json",
    "/models/ModelStyle_captionstyle_grid.json",
    "/models/ModelStyle_captionstyle_form.json",
    "/models/ModelStyle_cellstyle_grid.json",
    "/models/ModelStyle_controlstyle_grid.json",
    "/models/ModelStyle_controlstyle_form.json",
    "/models/ModelStyle_blockstyle_form.json",
    "/models/ModelStyle_Cust_Listing.json",
    "/models/ModelStyle_Cust_Listing.css"
  ],
  "Demo": [
    { "url": "jsHarmonyTutorials/ModelStyle_css", "title": "model.css" },
    { "url": "jsHarmonyTutorials/ModelStyle_tabpanelstyle?cust_id=2&action=update", "title": "model.tabpanelstyle" },
    { "url": "jsHarmonyTutorials/ModelStyle_tablestyle", "title": "model.tablestyle" },
    { "url": "jsHarmonyTutorials/ModelStyle_rowstyle", "title": "model.rowstyle" },
    { "url": "jsHarmonyTutorials/ModelStyle_formstyle?cust_id=1&action=update", "title": "model.formstyle" },
    { "url": "jsHarmonyTutorials/ModelStyle_captionstyle_grid", "title": "Grid: field.captionstyle / field.captionclass" },
    { "url": "jsHarmonyTutorials/ModelStyle_captionstyle_form?cust_id=1&action=update", "title": "Form: field.captionstyle / field.captionclass" },
    { "url": "jsHarmonyTutorials/ModelStyle_cellstyle_grid", "title": "Grid: field.cellstyle / field.cellclass" },
    { "url": "jsHarmonyTutorials/ModelStyle_controlstyle_grid", "title": "Grid: field.controlstyle / field.controlclass" },
    { "url": "jsHarmonyTutorials/ModelStyle_controlstyle_form?cust_id=1&action=update", "title": "Form: field.controlstyle / field.controlclass" },
    { "url": "jsHarmonyTutorials/ModelStyle_blockstyle_form?cust_id=1&action=update", "title": "Form: field.blockstyle / field.blockclass" },
    { "url": "jsHarmonyTutorials/ModelStyle_Cust_Listing", "title": "Custom CSS Files" }
  ]
}
</script>

<br/>
jsHarmony forms and grids are rendered using HTML and css.  A variety of properties are available for applying CSS styles.

<h3>Model Properties</h3>

<h3 data-level="2">model.css</h3>
The "css" property allows the user to apply any custom CSS selectors and styles.  A full stylesheet can be implemented in the "css" property.
The property value can either be a string or an array of strings.  Arrays of strings will be concatenated with spaces.
<pre>
{
  "css": [
    ".xbody_ModelStyle_Cust_Listing .xform_title { color: red; }",
    ".xbody .xactions a { color: red; }"
  ]
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_css?popup=1','Model Style - css')%>

<h3>Tab Properties</h3>

<h3 data-level="2">model.tabpanelstyle</h3>
The "tabpanelstyle" property is applied to the tab panel container.
<pre>
{
  "tabpanelstyle": "border: 2px dashed red;"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_tabpanelstyle?cust_id=2&action=update&popup=1','Model Style - tabpanelstyle')%>

<h3>Grid Properties</h3>

<h3 data-level="2">model.tablestyle / model.tableclass</h3>
The "tablestyle" and "tableclass" properties define the style of the grid's HTML table.
<pre>
{
  "tablestyle": "border: 2px solid red;",
  "tableclass": "class_name"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_tablestyle?popup=1','Model Style - tablestyle')%>

<h3 data-level="2">model.rowstyle / model.rowclass</h3>
The "rowstyle" and "rowclass" properties are applied to the "style" and "class" of each TR data row of the grid.<br/>
<br/>
EJS can be used to add dynamic styles based on the row index or row data.  EJS should be escaped with &lt;% ... %&gt;.  The following EJS variables are available:
<ul>
  <li><b>rowid</b> : The grid row index, starting from 0 as the first row</li>
  <li><b>datarow</b> : The current row's data.  Fields are accessible via datarow['FIELDNAME']</li>
  <li><b>data</b> : The full recordset.  Fields are accessible via data[rowid]['FIELDNAME']</li>
  <li><b>xejs</b> : xejs Helper functions</li>
  <li><b>jsh</b> : The jsHarmony client-side instance</li>
  <li><b>instance</b> : The ID of the jsHarmony client-side instance</li>
</ul>
Additionally, "even" and "odd" classes are automatically added to each grid row.<br/>
<br/>
The xejs.iif and xejs.case statements can be used for conditional styles:
<pre>
{
  "rowstyle": "&lt;%=xejs.iif(datarow['cust_sts']=='ACTIVE','color:red;','color:black;')%&gt;",
  "rowclass": "&lt;%-xejs.case(datarow['cust_sts']=='ACTIVE','xgrid_row_highlight','xgrid_row_disabled')%&gt;"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_rowstyle?popup=1','Model Style - rowstyle')%>
The following built-in row classes are available:<br/>
&nbsp;
<table class="xtbl" style="border-top:1px solid black;">
  <tbody>
    <tr class="xgrid_row_highlight"><td>xgrid_row_highlight</td></tr>
    <tr class="xgrid_row_disabled"><td>xgrid_row_disabled</td></tr>
    <tr class="xgrid_row_error"><td>xgrid_row_error</td></tr>
    <tr class="xgrid_row_none"><td>xgrid_row_none</td></tr>
    <tr class="xgrid_row_green"><td>xgrid_row_green</td></tr>
    <tr class="xgrid_row_yellow"><td>xgrid_row_yellow</td></tr>
    <tr class="xgrid_row_red"><td>xgrid_row_red</td></tr>
    <tr class="xgrid_row_gray"><td>xgrid_row_gray</td></tr>
  </tbody>
</table>

<h3>Form Properties</h3>

<h3 data-level="2">model.formstyle / model.formclass</h3>
The "formstyle" and "formclass" properties are applied to the DIV containing the form elements.
<pre>
{
  "formstyle": "border: 2px solid red;",
  "formclass": "class_name"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_formstyle?cust_id=1&action=update&popup=1','Model Style - formstyle')%>

<h3>Field Properties</h3>
Field properties can be defined for each field in the model.

<h3 data-level="2">field.captionstyle / field.captionclass</h3>
In form, form-m, and exec layouts, the "captionstyle" and "captionclass" properties are applied to the "style" and "class" of the field caption label.  In order to simplify formatting, the first defined field.captionstyle will be passed down to all subsequent fields that do not have field.captionstyle defined.<br/>
In grid layouts, the "captionstyle" and "captionclass" properties are applied to the "style" and "class" of the TH column header.
<pre>
{
  "captionstyle": "border: 4px dashed black;",
  "captionclass": "fieldWithErrors"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_captionstyle_grid','Model Style - captionstyle - grid')%>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_captionstyle_form?cust_id=1&action=update&popup=1','Model Style - captionstyle - form')%>

<h3 data-level="2">field.cellstyle / field.cellclass</h3>
On grid and multisel layouts, the "cellstyle" and "cellclass" properties are applied to the "style" and "class" of the TD data row cells.
<pre>
{
  "cellstyle": "border: 4px dashed black;",
  "cellclass": "fieldWithErrors"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_cellstyle_grid','Model Style - cellstyle - grid')%>

<h3 data-level="2">field.controlstyle / field.controlclass</h3>
In form, form-m, and exec layouts, the "controlstyle" and "controlclass" properties are applied to the "style" and "class" of the field's control.<br/>
In grid and multisel layouts, the "controlstyle" and "controlclass" properties are applied to the "style" and "class" of the field's control in each data row.
<pre>
{
  "controlstyle": "border: 4px dashed black;",
  "controlclass": "fieldWithErrors"
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_controlstyle_grid','Model Style - controlstyle - grid')%>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_controlstyle_form?cust_id=1&action=update&popup=1','Model Style - controlstyle - form')%>

<h3 data-level="2">field.blockstyle / field.blockclass</h3>
In form, form-m, and exec layouts, the "blockstyle" and "blockclass" properties are applied to the "style" and "class" of the div surrounding a field, if field.block = true.<br/>
<br/>
Form elements will normally be rendered in-line, without line breaks, unless field.block=true, or model.onecolumn=true.<br/>
<br/>
In grid and multisel layouts, the "blockstyle" and "blockclass" properties are not applicable.
<pre>
{
  {"control":"html","value":"Block HTML 1","block":true},
  {"control":"html","value":"Block HTML 2","block":true},
  {"control":"html","value":"Non-Block HTML 1"},
  {"control":"html","value":"Non-Block HTML 2"},
  {"name": "cust_name","caption": "Customer Name","block": true,
    "blockstyle": "border: 4px dashed black;",
    "blockclass": "fieldWithErrors"
  }
}
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_blockstyle_form?cust_id=1&action=update&popup=1','Model Style - blockstyle - form')%>

<h3 data-level="2">Line Breaks - field.nl / field.eol / field.block / field.nowrap / model.onecolumn</h3>
Line breaks can be added to fields using either the field.nl or field.eol properties.
<pre>
//Field properties

//New Line
{
  "nl": true //Insert a line break before this field
}

//End of Line
{
  "eol": true //Insert a line break after this field
}

//No Wrap
{
  "nowrap": true //Prevent a line-break between the caption and field
  // The nowrap property can be set to true, false, or "auto" (default)
}
</pre>
The field.block property will add a "div" wrapper around the field and caption, effectively adding a line-break before and after the field.  The field.block property will also squash / ignore the next field's "field.nl" property.
<pre>
//Field Blocks
{
  "block": true //Add a block element around the field, so that it will be rendered on its own line
}

//Blocks can be used with HTML elements
{
  "control": "html",
  "value": "<h1>Heading</h1>",
  "block": true
}

//Blocks can be used with Standard elements
{
  "name": "cust_id",
  "control": "textbox",
  "caption": "Customer ID",
  "block": true
}
</pre>
Setting "model.onecolumn" to true will automatically add line breaks to every field except for HTML controls.  Model.onecolumn will also adjust the captions of all the form fields to be the same width:
<pre>
//Model properties
{
  "layout": "form",
  "onecolumn": true
}
</pre>

<h3>Custom CSS Files</h3>
Custom CSS files can be defined either sitewide or per-model.

<h4>Sitewide CSS</h4>
The "/application.css" URL returns a public CSS file that contains most of the application's styles.
The application.css file concatenates all of the following files:
<ol>
  <li>jsHarmony/jsHarmony.css <i>(Core System Styles)</i></li>
  <li>Each file in jsh.Config.css_extensions</li>
  <li>jsHarmony/jsHarmony.theme.css <i>(Design Styles)</i></li>
  <li>For each module:<br/>
      &nbsp; &nbsp; module/models/public_css/* <i>(Module-specific Styles)</i></li>
  <li>application/models/public_css/* <i>(Application-specific Styles)</i></li>
</ol>
Any files located in the "models/public_css" folder will be added to the public application.css file.<br/>
<br/>
When creating a new theme / design for a site, the jsHarmony.theme.css file can be copied into the "models/public_css" folder, and used as a starting point for modifying the styles.<br/>
<br/>
Sitewide CSS files can contain EJS for dynamic server-side rendering.  The following EJS parameters are supported:
<ul>
  <li>
    <b>req</b> : Express Request object<br/>
    &nbsp; &nbsp; <b>req.jsh</b> : jsHarmony object<br/>
    &nbsp; &nbsp; <b>req.jshsite</b> - jsHarmonySite object</li>
  <li><b>rootcss</b> : The current jsHarmonySite's "rootcss" property, used for rendering multiple jsHarmony instances.</li>
</ul>

<h4>Per-Model CSS</h4>
Styles that are specific to a model can be part of the model definition, or in a MODELID.css file in the same folder as the model.
Per-model styles are not publicly accessible.
<h5>Cust_Listing.json</h5>
<pre>
{
  "css": ".xbody_Cust_Listing .xform_title { color: red; }"
}
</pre>
<h5>Cust_Listing.css</h5>
<pre>
.xformCust_Listing td { color: red; }
.xformCust_Listing thead tr { background-color: green; }
</pre>
<%-getScreenshot('jsHarmonyTutorials/ModelStyle_Cust_Listing?popup=1','Model Style')%>

<h3>CSS Architecture</h3>

<h4>Per-Model Tags</h4>
The jsHarmony page is composed of six primary HTML components:
<ul>
  <li><b>xhead</b>: Logo and menu bar</li>
  <li><b>xbody_TOPMODELCLASS</b>: SPA container - recreated each time the model is loaded</li>
  <li style="margin-left:20px;"><b>bcrumbs</b>: Breadcrumbs</li>
  <li style="margin-left:20px;"><b>xtitle</b>: Topmost title</li>
  <li style="margin-left:20px;"><b>xactions</b>: Buttons</li>
  <li style="margin-left:20px;"><b>xformMODELCLASS</b>: Each model's content</li>
</ul>

Each form and grid field has the classes: "xform_ctrl FIELDNAME xelemMODELCLASS".<br/>
<br/>
The MODELCLASS is defined by the "model.class" attribute.  This defaults to the model ID.

<h3>Popup Windows</h3>
The "popup" attribute defines whether a model should be opened as a popup window.  Popup windows are used in a variety of situations, for instance when a user clicks on an Insert or Update link.<br/>
<br/>
If the "popup" attribute is defined, the model is rendered without a menu or header.
<pre>
{
  "popup": [ WIDTH, HEIGHT ]
}
</pre>
WIDTH and HEIGHT are specified in pixels, for example:
<pre>
{
  "popup": [ 1000, 900 ]
}
</pre>

<h3>Fonts</h3>
System fonts are loaded via /fonts/jsHarmonyFonts.css.  The default fonts are stored in:
<pre>
jsHarmony/public/fonts/jsHarmonyFonts.css
</pre>
The default fonts are:
<ul>
  <li>Roboto 400</li>
  <li>PT Sans 400</li>
  <li>Material Icons 400</li>
</ul>
System fonts can be overridden by publishing a file to:
<pre>
application/public/fonts/jsHarmonyFonts.css
</pre>
It is recommended to embed fonts locally for faster performance and to reduce dependence on external systems.<br/>
<br/>
In order to use fonts in a report header/footer, they must be configured in the model.fonts property.


<h3>Stylus</h3>
Stylus code can be included in the project by adding a reference to the Stylus file in jsHarmony.Stylus.<br/>
Stylus files are dynamically compiled the first time they are requested, and cached for subsequent requests.
<pre>
jsh.Stylus['stylusID'] = {
  source: __dirname+'/stylus/filename.styl',//Path to the Stylus Source File
  path: '/filename.css',                    //Relative URL Path for the Express Router, resolves to https://domain.com/filename.css
  //roles: { SITEID: { "ROLE":"PERM" } },   //(Optional) Restrict access to the stylesheet by role
  public: true                              //Enable public unauthenticated access

  //css: 'Compiled Stylus CSS'              //After the Stylus file is compiled, it is cached in the "css" property
                                            //Once the "css" property is set, the Stylus file will not be compiled again,
                                            //and the value of the "css" property will be returned instead.
};
</pre>
