Shared javascripts and css for Corprio built-in web applications
npm install @corprio/core-tscorprio.js, which is a module that provides useful functions such as
"files": [ "dist" ]
`
As you can see, the dist folder contains three folders:
- css
- js
dist/css
This folder contains commonly used css files for Corprio apps.
The css files are distributed from the css folder.
Basically, each css file is copied over along with its minified version.
Below are brief descriptions of each file or folder in dist/css
corprio.css
Styles used by corprio.js
corprio-app.css
Styles used by corprio apps with the common layout navbar, sidebar.
This is used by partial viewss found in the Views/Shared of the Corprio.AspNetCore.Site project
corprio.bundle.min.css
This is the minified version by combining corprio.css and corprio-app.css into one file.
co-widgets.css
Styles used by corprio UI widgets such as the image uploader.
localizedFonts
This folder contains css files to set the right fonts for the page's language.
Below is an example of what TC.css might look like.
`
body{
font-family: 'Noto Sans', 'Noto Sans TC', Helvetica,Arial,sans-serif;
}
`
Depending on the thread's current culture, a different css file will be served.
C# helper functions can be found in LanguageHelper
so that apps can use it in the _Layout, like so.
`
`
To use LanguageHelper,
make sure the Nuget package for Corprio.AspNetCore.Site is included.
icons
The files in this folder are required by devextreme icons
dist/js
The main script in this folder is the corprio.js
or alternatively the corprio.bundle.js. This script contains
all the javascript helper functions that are commonly useful in our Corprio apps.
ts folder
This folder contains typescript modules and declarations that will be transpiled into javascript
for distribution.
$3
This is the main script that will transpile into corprio.js.
It is written as an ES6 module and aggregates by exporting from all modules.
Note that most exports are written as
`
export * as moduleName from './modules/moduleName.js'
`
so that calls can be made by corprio.moduleName.moduleFunction(). However due
to legacy reasons of how the corprio module used to be structured,
some exports are written as
`
export { moduleFunction } from './modules/moduleName.js'
`
to expose the function in corprio.moduleFunction()
Another note about the file name extension when importing and exporting.
Note that it is exported from moduleName.js and not moduleName.ts.
The reason is that ultimately, the typescript files will be transpiled into javascript files,
and in browsers, that extension is necessary for them to correctly request for the resources.
Typescript will not remap any .ts imports into .js for you since it breaks their
principles according to this thread https://github.com/microsoft/TypeScript/issues/16577#issuecomment-754941937
> We are not going to implement features, even under a commandline flag, that imply changing JS semantics by rewriting it during emit
$3
This folder contains typescript ES6 modules that will be aggregated by corprio.ts.
$3
Scripts in this folder aims to add extension methods to existing javascript prototypes.
Because the script nature is not a module (no import or export keywords in file),
when using the extension, import it like so
`
import '../extensions/date.extensions.js'
`
$3
Modules in this folder aims to export another module that has been imported dynamically.
That other module may be a third-party module that is large in size, but not used often,
and therefore you wish to only import it when needed.
To learn more about dynamic imports: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
In the example below, the exported function retrieve will return a promise,
which will resolve to whatever the module exceljs provides.
exceljs.ts
`
function resolve() {
return import('exceljs')
.then((ExcelJS) => {
return ExcelJS;
})
}
export { resolve }
`
It can then be used in another file like this:
`
import * as exceljs from '../dynamicImports/exceljs.js'
exceljs.retrieve().then((ExcelJS) => {
//do something with ExcelJS
});
`
The reason for separating each dynamic import into another ES module is that it allows for
treeshaking by webpack later on. Webpack treeshaking only works for ES modules currently.
Therefore if we do not separate the dynamic import, that module will also be included even
if it never had been used.
$3
This folder contains modules that load globalized messages used in corprio.js.
$3
This folder contains modules that load globalized messages used by Devextreme widgets.
$3
This folder contains modules that will load globalized messages in a certain culture
for both corprio.js and Devextreme widgets. For example, in a page that is going to be
served in simplified Chinese, and uses corprio.js or Devextreme widgets, you may
include the script in the head.
`
`
Note that type="module" must be included since the script has import statements,
which makes it a module.
To make it easier to use with the C# projects, use the LanguageHelper from the
Nuget package for this project Corprio.AspNetCore.Site
`
`
$3
These scripts provide UI widgets such as the image uploader that we use in corprio.
They operate kind of like Devextreme's UI widgets. One thing that is different is
that even if you include the corprio.js script, you cannot directly use the
coWidgets accessing under jQuery $('#id').coImageUploader(...).
That is because corprio.js has no side-effects. It only exports a bunch of functions,
but doesn't run anything besides that. This is so that corprio.js can be safely
tree-shaken.
So before you use any coWidgets, first initialize them by
`
corprio.coWidgets.useCoWidgets();
`
Or if you are only the coImageUploader
`
corprio.coWidgets.useCoImageUploader();
`
After that you can use the coWidgets by attaching them to an Html element
`
$('#id').coImageUploader({ optionName1: optionValue1, optionName2: optionValue2 })
`
Then access the instance like so
`
$('#id').coImageUploader('instance')
`
$3
This folder contains type declaration files, which are files ending in .d.ts.
These are not modules and do not transpile into javascript. They only exist in the typescript
world. The purpose of these files is to define the shape of certain modules or objects,
and declare them to be existent.
We must also tell typescript which type declaration files to include in our tsconfig.json.
`
{
"compilerOptions": {
"types": [ "jquery", "file-saver", "bootstrap", "node" ]
},
"include": [
"ts/*/"
]
}
`
Here we told them to include types for "jquery", "file-saver", "bootstrap", "node", which
comes from node_modules/@types by default. And they types are there because we have included
them in the package.json. Then, in the include config, we told typescript we want
everything in the ts folder to be included in the project, so they will transpile, type-check,
work with everything in there. The ts folder includes ts/declarations which is why
the declarations will be automatically included.
#### devextreme
The modules for corprio.js use a lot of devextreme library but it never imported the code
for it anywhere. It is just assumed that whoever is using corprio.js will also have
included the scripts for devextreme elsewhere. This is fine since javascript doesn't care
to type-check, but for typescript, it needs to know that devextreme library has somehow
been included in order to type-check in your editor. That's what
dx.all.d.ts does to declare devextreme types
to exist. The rest of the files in this folder are also used by devextreme as its
dependencies.
The origin of these files are from the folder where Devextreme is installed,
in the Lib folder. They are simply copied over into this project.
#### schema
Files in the folder aims to define the shape of objects used in Corprio, namely
the shape of objects pertaining to Corprio API, and the shape of StaticData.
##### Corprio API
The Corprio API consists of request paths, request methods, data it takes,
and data it returns. All of these have a predefined shape, as stated in our swagger
documentation. We can make use of these shapes with typescript to make ajax calls
a lot easier.
The schema.d.ts file declares the shape of Corprio
API so we can use the typings in our scripts. This file is generated from the
swagger.json which has been manually downloaded
from https://api.corprio.com/swagger/v1/swagger.json. Currently, the entire process
is manual, from downloading the json file to running a command that generates the
d.ts file. Instructions on how to generate the file are detailed in the
generate-schema.txt.
Now we can access the types by importing them.
`
import type { models, paths } from "@corprio/aspnetcore-site/dist/js/corprio"
//get intellisense for the data model SalesOrderLine
var salesOrderLine: models['SalesOrderLine'];
//get intellisense for the requestBody for creating a product by ajax
var productData: paths['/v1/Product/{organizationID}']['post']['requestBody']['content']['application/json']
`
models and paths are exported from corprio.js which makes the typings available.
The models.d.ts simply re-exports the data model types
under another alias.
##### StaticData
In corprio.js there are often references to the object StaticData but this
variable has not been imported or defined anywhere in the module.
That's why we have defined the shape and declared it in staticData.d.ts so typescript will know it exists.
But what does it do, and where does it come from?
StaticData contains "static" data of the geographic world such as country list,
currency list, phone number formats, etc. However, if the object was truly static,
there should be a module or json file to define this object, and thus
we can simply import it and there won't be the need to declare it.
So why is it declared? Because it isn't really static, despite the misleading name.
One reason is that the country list contains globalized names of the country,
and so the object must be generated run-time depending on the thread's culture.
In fact, it is generated on server-side based on data models defined in the
Corprio.Global project, so as to keep the the server-side and client-side
static data in sync.
But aside from that, the main reason is that StaticData also serves data
about the current organization being requested and provides it
under StaticData.OrgInfo. And that's not static at all, and therefore must be
generated server-side per request. That's being done by
StaticDataViewComponent in the Corprio.AspNetCore.Site project, which is a
view component that generates and inserts the following
`
`
into the view, if the view file contains the view component
`
`
Now that we know where the actual StaticData object comes from, how is the
staticData.d.ts able to know its shape? It doesn't. I generated it manually with
the npm package dts-generator.
I copied the contents of StaticData at run-time then pasted
it into the command to generate the .d.ts file.
#### corprio
Files in this folder aims to declare global variables for corprio.js.
That includes two things: global variables referenced by corprio.js that we want
typescript to know about, and the very corprio module itself, if we want it
to be recognized globally.
##### globalVariables.d.ts
This file declares variables to exist globally in the Window scope, that for one reason
or another exists by some other way. This includes StaticData mentioned earlier.
It also includes some other variables that are here for legacy reasons. It is not a good
practice, since there will be no way to ensure those dependencies actually exist at
run-time. For future, please aim to have all dependencies imported by ES module syntax.
##### corprio.d.ts
This file declares corprio to exist and defines its shape for corprio.js.
Note: this is also here for legacy reasons. In the past, corprio.bundle.js
used to be written as an immediately-invoked factory function that we set to a
var corprio. This corprio.bundle.js file is then included in the _Layout,
and then we use it in our scripts everywhere else, assuming the corprio variable
to exist, and getting zero intellisense for it.
Now it is rewritten to be ES modules. When using it from other apps, we can simply
import the module into our script.
`
import * as corprio from "@corprio/core-ts/dist/js/corprio"
`
This not only ensures that corprio exists in the context, but also
allows for treeshaking with webpack later on. When using it this way, there is no need for the
corprio.d.ts file to declare it.
However, because of our generous usage of referencing corprio everywhere in the past,
it will be difficult to remove the corprio.bundle.js script from _Layout because something
is probably going to die without it. And since it is still globally included,
this file corprio.d.ts exists to declare it, so that you get intellisense in your
typescript files.
##### coWidgets.d.ts
This is the declaration file for corprio UI widget components, to make typescript
intellisense available when using coWidgets as jQuery extension methods.
Usage of the corprio module
Let's look at how you may use corprio module in another project.
There are two ways: corprio.js and corprio.bundle.js
corprio.js is an ES module, and you may use it by importing them into your scripts.
This is the preferred way to use it, since ES modules are tree-shakable.
Alternatively you may use corprio.bundle.js in your project.
This may be the case if you want include it once and be able to use it globally everywhere.
The downside is that corprio.bundle.js is a UMD module which is not tree-shakable.
Also, dependencies are not as clear as if you use ES modules. Generally it is not the best
idea to inject variables into your javascript global scope.
corprio.bundle.min.js is just the minified version of corprio.bundle.js. Use the minified
version for production. Use the non-minified version for development, so that you have the
site-maps and variable names kept in tact to make debugging much easier.
$3
The scripts are published to npm under the package name @corprio/core-ts under the organization 'corprio'.
To publish, follow the steps below.
1. Make sure you have permission to publish to the organization 'corprio'.
1. Bump up the version number in package.json according to semver rules.
1. Build the project in Release mode in Visual Studio, so that the dist folder is updated.
1. Login npm by running the command
`
npm login
`
5. Publish to npm by running the command in the root folder of the project
`
npm publish --access public
`
$3
#### 1. Install @corprio/core-ts
First, make sure you have this package installed.
`
npm i @corprio/core-ts
`
#### 2. Include the type definitions in your tsconfig.json
If you're using typescript, include type definitions for corprio.js
`
{
"compilerOptions": {
"types": ["jquery", "file-saver", "bootstrap", "node"]
},
"include": [
"node_modules/@corprio/core-ts/dist/js/*/"
],
"exclude": [
//corprio.d.ts is excluded if you are using corprio.js by ES module import syntax
"node_modules/@corprio/core-ts/dist/js/declarations/corprio/corprio.d.ts"
]
}
`
#### 3. Import corprio.js in your script
In your script file, import the module and use.
In myScript.js:
`
import * as corprio from "@corprio/core-ts/dist/js/corprio"
//use its functions
var orgID = corprio.getCurrentOrgID();
`
Make sure that when you're including the script in your html, to write type = "module"
unless you will further process your script.
`
`
#### 4. Treeshake your script with webpack (optional)
Since you are probably not using every function imported from corprio,
it is a good idea to treeshake your scripts so that only what is used is being outputted.
More details in https://webpack.js.org/guides/tree-shaking/
$3
#### 1. Install @corprio/core-ts
First, make sure you have this package installed.
`
npm i @corprio/core-ts
`
#### 2. Include the type definitions in your tsconfig.json
If you're using typescript, include type definitions for corprio.bundle.js
`
{
"compilerOptions": {
"types": ["jquery"]
},
"include": [
"node_modules/@corprio/core-ts/dist/js/*/"
]
}
`
#### 3. Include the corprio.bundle.js in html
In your html file, include the script
`
`
Or if it's an cshtml file such as your _Layout, you'll have to escape the '@' character
`
`
#### 4. Use in your scripts
You may then call functions from corprio in the same page, since it is already
available in the global scope.
Product Tour
Follow the steps below to add Product Tour feature in an application.
1. Add a service class which implements IProductTourService in the application
1. Register the service class in ConfigureServices of the Startup class. For example
services.AddProductTour
1. On all views which need the product tour function, run the script inside onload
corprio.page.initTour({
Recommend to name the tour using the syntax [application].[controller].[index] to avoid conflicts between different applications. This is because the name is saved in cookies after the tour is loaded so that it will not run again automatically next time.
$3
1. Standard tour steps have been defined in the ProductTour class which can be shared by applications which use the standard layouts.
1. The library driver.js hard coded the font family in its CSS. This makes the icons of devextreme and fontawesome cannot be shown. To resolve this, add special class when using icons in the tour content.
For fontawesome, add the class fontawesome, e.g.
For icons in devextreme, add the class dxicons, e.g.
Source Code
The source files are located in the src folder.
The main code files are:
/src/css - source css files
/src/ts - source typescript files]
Building the project
To build the project, open the project file Corprio.CoreTS.esproj in Visual Studio and then build the project.
The project is configured to run commands defined in package.json.
Corprio.CoreTS.esproj files:
`
`
package.json commands:
`
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc --build",
"build:release": "tsc --build && webpack --mode=production",
"build:debug": "tsc --build && webpack --mode=development",
"clean": "tsc --build --clean && webpack --mode=production"
},
`
The commands do the following:
- tsc --build - transpile typescript files into javascript files and save in the dist/js folder
- webpack --mode=production`