Skip to content

Major version 5.0 ‐ ES6 ESM and TypeScript Support

Ghislain B edited this page Apr 7, 2024 · 48 revisions

Welcome to the modern world 🌐

This new version is all about modernizing the project (for modern tooling and a modern look), it brings a lot of changes, mainly:

  • Node 18 is required (since Node 16 is EOL for a few months already)
  • Migrated to TypeScript build which also provides Types (d.ts)
  • ES6 / ESM support while still providing standalone scripts (IIFE)
  • New Modern Alpine Theme (inspired by Ag-Grid) that can be customized via SASS and/or CSS variables (Classic Theme is also still available)
  • Replaced (removed) all images with a small subset of SVGs that can also be colorized via pure CSS (see SlickGrid Icons)

With this latest version release, we can now say that our journey into modernizing the project is, for the most part, completed (it started with v3 by removing jQueryUI, followed by jQuery removal in v4 and today we modernize the project in v5). Finally all of that being said, we do not expect any major (aka breaking) changes & versions for the foreseeable future... we're pretty much done!!!

See Migration below for instructions or keep reading for more details about this release.

ES6 / ESM

Bringing ES6/ESM with TypeScript support was the main focus of this release and it also closes our oldest issue #41 while still providing standalone scripts for users who still prefer them. However please note that in order to support this new rewrite, we needed to change the folder structure (all source files were moved to src/ and all builds now exist in the dist/), this also mean that every single users will have to update their script imports to the new dist/[target] folders in order to use SlickGrid. Also note that all users will no longer be able to use any of the core file directly since they are now TypeScript files.

We now use esbuild in order to support multiple build targets and the new folder structure, we now have the following build output targets

  • iife to still provide standalone <script> that SlickGrid provided for years (located under dist/browser with ES2018 build target)
  • cjs for CommonJS build for NodeJS legacy loading using require(...) (located under dist/cjs with ES2020 build target)
  • esm for ES6/ESM support which will give us Tree Shaking support (located under dist/esm with ES2020 build target)

Note I did a lot of research to make sure that we get the smallest possible builds for each build formats, for example I did not want to have code that isn't used by the iife format but required for esm and vice versa (i.e. import are used by ESM but not at all used by iife). It took me a while to find the best approach, I even asked on SO with this question: esbuild hybrid plugin to bundle multiple files iife and single esm bundle and opened an issue on esbuild as well. I eventually made it all work with an hybrid approach that is the smallest footprint possible for each build types, woohoo 🚀

TypeScript

We are now using esbuild, which supports TypeScript out of the box and is providing us with a few benefits

  • by using TypeScript we can spot errors early before publishing any new release when bundling the code (we did actually spot a few small errors in the code)
  • provide TS Types .d.ts (interfaces, enums, types, ...)
  • TypeScript extendable (see below for some examples)
    • we used protected everywhere to make it easily extendable (instead of private)
    • we now also provide TS Generics to optionally define your items interface via SlickGrid<TData> and/or SlickDataView<TData>, which then flows to all SlickGrid/DataView sub-methods

Note if you are using TypeScript and are saving it as a .ts file, then make sure to add types to your columns (i.e.: let columns: Column[]) or else you might get TypeScript error as shown in this Stack Overflow. Adding the GridOption type is also a good idea to make sure that you use only valid options.

Note also make sure that your import is from 'slickgrid'; not slickgrid/* since that can also cause issues.

Here's a quick example of how we can now use TypeScript with SlickGrid (see TypeScript Examples Wiki for more details)

// example.ts (TypeScript file)
import { Column, GridOption, SlickDataView, SlickGrid } from 'slickgrid';

let data: any[] = []; // or `data: User[] = [];` with a User interface  then provide it as a Generic to SlickGrid (see below)
let columns: Column[] = [{ id: 'firstName', name: 'First Name', field: 'firstName' }, ...]; // or `Column<User>[]` if you want add column type
let options: GridOption = { enableCellNavigation: true, ... };

let grid = new SlickGrid('#myGrid', data, columns, options);
// let grid = new SlickGrid<User>('#myGrid', data, columns, options); // or with a User provided to SlickGrid as Generic type

// similarly for DataView
let dataView = new SlickDataView({ inlineFilter: true });
// let dataView = new SlickDataView<User>({ inlineFilter: true }); // same Generic can be used as data interface

Styling

We have a new optional Alpine Theme which is now used by most examples (not all of them), if you wanted to compare both the classic (previous theme) and the new Alpine Theme, you could just go to the new ESM Example and toggle the 2 themes by using associated Theme buttons.

Contributions

We welcome any new contributions, for example it shouldn't be too hard to add a dark Alpine theme and a contribution would be more than welcome. Please do remember that this is an Open Source project and most of this work is achieved in our spare time (for free) and so we welcome any contributions but please be respectful.

Migration

Deprecated code

Please note that to align all Menu plugins, we decided to rename all menu item array lists as commandItems, these changes are preferred but the old names will still work for the moment, they will only be removed in a future major version (if it ever happen). For the moment, we will show console warnings to the user when using deprecated names. The changes are focused on just these 2 plugins:

  • Header Menu
    • renamed items to commandItems
  • Grid Menu
    • renamed customItems to commandItems
    • renamed customTitle to commandTitle

Deprecated code & removed

DataView setAggregators and groupBy methods were marked as deprecated many years ago and so we removed them in this release. They were replaced by setGrouping, so just make sure that you use the newer setGrouping method if you haven't already.

We also no longer need any of the firebugx code, and they were all removed (that is because all browsers now have much improved developer tools).

All images were replaced by SVGs

All images (.gif, .png, ...) were deleted and replaced by a small subset of SVGs icons in pure CSS (icons are prefixed with sgi keyword for "Slick Grid Icons") which can also be colorized via CSS (see SlickGrid Icons page).

We used the technique that Anthony Fu, creator of UnoCSS, wrote in his article Reimagine Atomic CSS, this article was very helpful to learn how to easily colorize SVG with pure CSS.

Some might ask, why use SVGs instead of maybe Data URI or something else? You can read this article Probably Don’t Base64 SVG to understand why SVG are simply better and much smaller than images or fonts.

For example, if you were using images in DraggableGrouping (or any other plugins), you might want to replace some of the images by the new SlickGrid SVGs by using .sgi CSS classes

const draggableGrouping = new Slick.DraggableGrouping({
-  deleteIconImage:'../images/delete.png', 
-  groupIconImage: '../images/column-grouping.png',
+  deleteIconCssClass: 'sgi sgi-close',
+  groupIconCssClass: 'sgi sgi-drag-vertical'
});

Draggable Grouping

Since we migrated to TypeScript Classes and no longer use any global var, however please that note that this caused a scoping issue when using the DraggableGrouping plugin, so you will need to add a .bind(...) on the plugin instance to point it to the correct scope.

const draggableGrouping = new Slick.DraggableGrouping();
const gridOptions = {
-   enableColumnReorder: draggableGrouping.getSetupColumnReorder(draggableGrouping)
+   enableColumnReorder: draggableGrouping.getSetupColumnReorder.bind(draggableGrouping)
    createPreHeaderPanel: true,
    showPreHeaderPanel: true,
    preHeaderPanelHeight: 25,
}

Standalone scripts (iife)

For users who still want to use standalone <script>, the migration is quite simple and you will simply have to update your script locations and that's about it. Using standalone scripts will keep providing users with independent scripts the same as before (slick.grid.js, plugins/slick.headermenu.js, ...) but from a different location dist/browser/...

Note it is very important that slick.core.js is imported first because that is the script that creates the Slick namespace. The order might not have been important in the past, but it is today in order to support all 3 build types.

- <script src="slickgrid/slick.core.js"></script>
- <script src="slickgrid/slick.grid.js"></script>
+ <script src="slickgrid/dist/browser/slick.core.js"></script>
+ <script src="slickgrid/dist/browser/slick.grid.js"></script>

or from a CDN like JsDelivr

<link href="https://cdn.jsdelivr.net/npm/slickgrid@5.9.0/dist/styles/css/slick-alpine-theme.min.css" rel="stylesheet">

<script src="https://cdn.jsdelivr.net/npm/slickgrid@5.9.0/dist/browser/slick.core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/slickgrid@5.9.0/dist/browser/slick.grid.min.js"></script>

ES6 / ESM (cjs, esm)

By using esbuild we can provide multiple new targets (iife same as before and our new cjs, esm), the last 2 are the new ones for the project which allows us to close our oldest issue #41

Note all core files and plugins are exported with the Slick prefix (e.g. SlickGrid, SlickDataView, SlickColumnPicker, ...) with some exceptions (Aggregators, Editors, Formatters and all enums like ColAutosizeMode, RowSelectionMode, ...)

For a demo, you can take a look at this ESM - Example 4 or our new awesome demo of a Realtime Trading - High Frequency Update dashboard.

- <script src="slickgrid/slick.core.js"></script>
- <script src="slickgrid/slick.grid.js"></script>

+ <script type="module">
+ import {
+   Editors,
+   Formatters,
+   SlickGlobalEditorLock,
+   SlickRowSelectionModel,
+   SlickColumnPicker,
+   SlickDataView,
+   SlickGridPager,
+   SlickGrid,
+   Utils,
+ } from 'slickgrid';

- const dv = new Slick.DataView();
- const grid = new Slick.Grid('#myGrid', dv, columns, options);

// the only difference is the "." which is removed, there's no more Slick namespace
+ const dataView = new SlickDataView();
+ const grid = new SlickGrid('#myGrid', dataView, columns, options);

Note since SortableJS is a hard dependency, you also need to make sure that you import it and you assign it to the window object (unless you load it through <script> if so then there's nothing to do). Another way would to be to configure it in your WebPack or Vite config to make it globally available.

+ import Sortable from 'sortablejs';
+ window.Sortable = Sortable;

Styling

With the new SlickGrid Alpine Theme, if you are using it, we needed an extra (but optional) CSS class to identify the grid div container, we opted to use slick-container and even though it is optional, it's probably better to add it to your grids. This extra container CSS class is optional but some of the styling from the new Alpine Theme might not show up correctly without it.

For example, the grid border outline requires this container class to exist for showing the border correctly

- <div id="myGrid" style="width:100%;height:500px;"></div>
+ <div id="myGrid" class="slick-container" style="width:100%;height:500px;"></div>

We now also use CSS flexbox for some part of the grid, like the column headers and that could help in supporting RTL.

CSS / SASS variables

With the new Alpine Theme, we went all in and we also provide both CSS and SASS variables for easy customization, both of them can be found under the dist/styles/css or dist/styles/sass folders. For example if you want to use SASS, you can simply import it.

See available SASS variables, also note that the CSS variables are named with the same name except that you simply need to replace the $ prefix with -- prefix (e.g.: for SASS $alpine-font-size and for CSS --alpine-font-size)

Note since CSS Variables offers a fallback when first option is not found, we can use that to our advantage to offer CSS variables as the first choice and SASS variables as a fallback. This mean that if the user is defining both a CSS & SASS variable using the same name, then the CSS variable will win because the SASS is a fallback, however if only a SASS variable is defined then the SASS variable will be used.

with CSS (located under dist/styles/css)
/* optional CSS variables can be used */
:root {
  --alpine-font-size:  14px;
  --alpine-font-color: #111;
}

// import the new Alpine Theme
@import 'slickgrid/dist/styles/css/slick-alpine-theme.css';

// or import the old Classic Theme
@import 'slickgrid/dist/styles/css/slick-default-theme.css';
with SASS (located under dist/styles/sass)
/* change any SASS variables before loading the theme */
$alpine-font-size:  14px;
$alpine-font-color: #111;

// use the new Alpine Theme (currently only SASS theme available)
@import 'slickgrid/dist/styles/sass/slick-alpine-theme.scss';

Other Ports / Platforms Available

Below is a list of other ports which extends from this SlickGrid fork and could be used when developing for specific platforms


TypeScript Extendable

Since we now use TypeScript, we can now provide extra features for free, we now have Types (interfaces, enums, types, ...), Generics and it's also extendable as shown below.

SlickGrid & SlickDataView Generics

We added optional TS Generics to provide the item data context interface on the SlickGrid and/or SlickDataView so that you can call any methods from them and automatically get the associated item interface type for them.

import { SlickGrid, SlickDataView } from 'slickgrid';

interface User {
  firstName: string;
  lastName: string;
  age: number;
}
// Generics on SlickGrid
const grid = new SlickGrid<User>('#myGrid', data, columns, options);
const item = grid.getDataItem(0); // item will be User TS type

// or Generics on SlickDataView
const dataView = new SlickDataView<User>();
const grid = new SlickGrid('#myGrid', dataView, columns, options);
const item = dataView.getItemByIdx(0); // item will be User TS type

The SlickGrid class actually has 3 optional Generics, but you will rarely use the last 2. The 1st Generic is the item type (as shown above), the 2nd is if you want to extend the Column interface and finally the 3rd and last Generic is if you want to extend the GridOption interface (these extra Generics were mainly added for Slickgrid-Universal since that library also has extra Column & GridOption properties)

Class extends

Since we opted to use protected (instead of private) across all our TypeScript code, it makes it easy to extends (or override) any of these TS classes whenever you wish to customize or add extra methods on any of the SlickGrid classes (not just core files but also Controls and Plugins as well).

// for example let's extend Slick DataView
import { SlickDataView } from 'slickgrid';

class CustomSlickDataView  extends SlickDataView {
  // for example let's add a custom event on the destroy method
  onDestroy = new SlickEvent();

  constructor() {
    super();
  }

  destroy() {
    super.destroy();
    this.onDestroy.notify();
  }
}

// use our new extended DataView
const dv = new CustomSlickDataView();
dv.onDestroy.subscribe(() => console.log('DataView was destroyed'));

Final Note

That's it, enjoy the lib and feel free to contribute to the project. If you encounter any problems, create a new issue or a Discussion. Please remember to keep Questions on Stack Overflow, cheers ⭐🍺🚀

Quick Survey ✨

We made a quick little poll for fun, it would be nice to hear from you, thanks for taking the time to participate and tell us what you like. Cheers

What do you think was the most exciting change(s) for you?

Clone this wiki locally