Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 91c2726

Browse files
committed
feat: add Dark Mode grid option
1 parent 34e9614 commit 91c2726

15 files changed

+483
-167
lines changed

docs/TOC.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
## Styling
1010

11+
* [Dark Mode](styling/dark-mode.md)
1112
* [Styling CSS/SASS/Themes](styling/styling.md)
1213
* [SVG Icons](styling/SVG-Icons.md)
1314

docs/styling/dark-mode.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
## Dark Mode
2+
3+
When enabled (defaults to false), it will show the grid in Dark Mode by adding `slick-dark-mode` CSS class to the grid. Note that it is defined as a grid option because the grid uses a few elements that could be appended to the DOM `body` (e.g. ColumnPicker, GridMenu, LongTextEditor, CompositeEditorModal, ...) and when Dark Mode is enabled, it needs to advise all of these features that we are using Dark Mode (or Light Mode by default). So whenever any of these features are in play, and before it is appended to the `body`, it will add a `slick-dark-mode` (or `ms-dark-mode` for ms-select) CSS class to that element to let it know that we are in Dark Mode.
4+
5+
6+
### Toggle Light/Dark Mode
7+
8+
You can easily toggle light/dark mode by using `grid.setOptions()`
9+
10+
```ts
11+
export class MyDemo {
12+
isDarkModeEnabled = false;
13+
gridOptions: GridOption;
14+
15+
prepareGrid() {
16+
this.gridOptions = {
17+
// ...
18+
darkMode: this.isDarkModeEnabled;
19+
}
20+
}
21+
22+
toggleDarkMode() {
23+
this.isDarkModeEnabled = !this.isDarkModeEnabled;
24+
this.sgb.slickGrid?.setOptions({ darkMode: this.isDarkModeEnabled });
25+
26+
// optionally update your local grid options as well
27+
this.gridOptions = { ...this.gridOptions, darkMode: this.isDarkModeEnabled };
28+
}
29+
}
30+
```
31+
32+
### How to Auto-Detect Dark Mode?
33+
34+
By default the grid will **not** automatically enable Dark Mode, neither read the browser's color scheme (the reason are mentioned in the description above). However, you could implement your own code to detect the color scheme (for modern browser only) when loading your browser and set it in your grid options. You can see a demo of that in the first grid of [Example 1](https://ghiscoding.github.io/slickgrid-universal/#/example01)
35+
36+
```ts
37+
export class MyDemo {
38+
gridOptions: GridOption;
39+
40+
// auto-detect browser's color scheme function
41+
isBrowserDarkModeEnabled() {
42+
return window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
43+
}
44+
45+
prepareGrid() {
46+
this.gridOptions = {
47+
// ...
48+
darkMode: this.isBrowserDarkModeEnabled();
49+
}
50+
}
51+
}
52+
```
53+
54+
### Composite Editor Modal (for Bootstrap users)
55+
56+
For `Bootstrap` users, it will also require the developer to add a `data-bs-theme="dark"` attribute which is also another reason why we added `darkMode` as a grid option. So for Bootstrap users, you will have to add this required attribute by yourself for the Dark Mode to display properly. If you forget to add this attribute, you might see some of the filter inputs and other sections displayed with a white background instead of an expected dark gray backgroud.
57+
58+
> **Note** the `onRendered` is a new lifecycle callback of Composite Editor Modal that was added specifically for this Bootstrap use case
59+
60+
```ts
61+
this.compositeEditorInstance?.openDetails({
62+
// ...
63+
onRendered: (modalElm) => modalElm.dataset.bsTheme = 'dark',
64+
});
65+
```

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@
5050
},
5151
"dependencies": {
5252
"@ngx-translate/core": "^15.0.0",
53-
"@slickgrid-universal/common": "~4.4.1",
54-
"@slickgrid-universal/custom-footer-component": "~4.4.1",
55-
"@slickgrid-universal/empty-warning-component": "~4.4.1",
56-
"@slickgrid-universal/event-pub-sub": "~4.4.1",
57-
"@slickgrid-universal/pagination-component": "~4.4.1",
58-
"@slickgrid-universal/row-detail-view-plugin": "~4.4.1",
59-
"@slickgrid-universal/rxjs-observable": "~4.4.1",
53+
"@slickgrid-universal/common": "~4.5.0",
54+
"@slickgrid-universal/custom-footer-component": "~4.5.0",
55+
"@slickgrid-universal/empty-warning-component": "~4.5.0",
56+
"@slickgrid-universal/event-pub-sub": "~4.5.0",
57+
"@slickgrid-universal/pagination-component": "~4.5.0",
58+
"@slickgrid-universal/row-detail-view-plugin": "~4.5.0",
59+
"@slickgrid-universal/rxjs-observable": "~4.5.0",
6060
"dequal": "^2.0.3",
6161
"dompurify": "^3.0.9",
6262
"rxjs": "^7.8.1",
@@ -87,12 +87,12 @@
8787
"@ngx-translate/http-loader": "^8.0.0",
8888
"@popperjs/core": "^2.11.8",
8989
"@release-it/conventional-changelog": "^8.0.1",
90-
"@slickgrid-universal/composite-editor-component": "~4.4.1",
91-
"@slickgrid-universal/custom-tooltip-plugin": "~4.4.1",
92-
"@slickgrid-universal/excel-export": "~4.4.1",
93-
"@slickgrid-universal/graphql": "~4.4.1",
94-
"@slickgrid-universal/odata": "~4.4.1",
95-
"@slickgrid-universal/text-export": "~4.4.1",
90+
"@slickgrid-universal/composite-editor-component": "~4.5.0",
91+
"@slickgrid-universal/custom-tooltip-plugin": "~4.5.0",
92+
"@slickgrid-universal/excel-export": "~4.5.0",
93+
"@slickgrid-universal/graphql": "~4.5.0",
94+
"@slickgrid-universal/odata": "~4.5.0",
95+
"@slickgrid-universal/text-export": "~4.5.0",
9696
"@types/dompurify": "^3.0.5",
9797
"@types/flatpickr": "^3.1.2",
9898
"@types/fnando__sparkline": "^0.3.7",

src/app/examples/grid-basic.component.html

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,32 @@ <h2>
1111
</h2>
1212
<div class="subtitle" [innerHTML]="subTitle"></div>
1313

14-
<h3>Grid 1</h3>
15-
<angular-slickgrid gridId="grid1"
16-
[columnDefinitions]="columnDefinitions1"
17-
[gridOptions]="gridOptions1"
18-
[dataset]="dataset1">
19-
</angular-slickgrid>
14+
<h3>
15+
<div class="column">
16+
<span class="mr-3">Grid 1</span>
17+
<button class="btn btn-outline-secondary btn-sm ms-2" (click)="toggleDarkModeGrid1()" data-test="toggle-dark-mode">
18+
<span>Toggle Dark Mode</span>
19+
</button>
20+
</div>
21+
</h3>
22+
23+
<div class="grid-container1">
24+
<angular-slickgrid gridId="grid1"
25+
[columnDefinitions]="columnDefinitions1"
26+
[gridOptions]="gridOptions1"
27+
[dataset]="dataset1"
28+
(onAngularGridCreated)="angularGridReady1($event.detail)">
29+
</angular-slickgrid>
30+
</div>
2031

2132
<hr />
2233

2334
<h3>Grid 2 <small>(with local Pagination)</small></h3>
24-
<angular-slickgrid gridId="grid2"
25-
[columnDefinitions]="columnDefinitions2"
26-
[gridOptions]="gridOptions2"
27-
[dataset]="dataset2">
28-
</angular-slickgrid>
35+
<div class="grid-container">
36+
<angular-slickgrid gridId="grid2"
37+
[columnDefinitions]="columnDefinitions2"
38+
[gridOptions]="gridOptions2"
39+
[dataset]="dataset2">
40+
</angular-slickgrid>
41+
</div>
2942
</div>

src/app/examples/grid-basic.component.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { Component, OnInit } from '@angular/core';
2-
import { Column, GridOption, Formatters } from './../modules/angular-slickgrid';
1+
import { Component, OnDestroy, OnInit } from '@angular/core';
2+
import { Column, GridOption, Formatters, AngularGridInstance } from './../modules/angular-slickgrid';
33

44
const NB_ITEMS = 995;
55

66
@Component({
77
templateUrl: './grid-basic.component.html'
88
})
9-
export class GridBasicComponent implements OnInit {
9+
export class GridBasicComponent implements OnDestroy, OnInit {
10+
private _darkModeGrid1 = false;
1011
title = 'Example 1: Basic Grids';
1112
subTitle = `
1213
Basic Grids with fixed sizes (800 x 225) set by "gridHeight" &amp; "gridWidth"
@@ -15,6 +16,7 @@ export class GridBasicComponent implements OnInit {
1516
</ul>
1617
`;
1718

19+
angularGrid1!: AngularGridInstance;
1820
columnDefinitions1: Column[] = [];
1921
columnDefinitions2: Column[] = [];
2022
gridOptions1!: GridOption;
@@ -23,6 +25,23 @@ export class GridBasicComponent implements OnInit {
2325
dataset2!: any[];
2426

2527
ngOnInit(): void {
28+
this.prepareGrid();
29+
}
30+
31+
ngOnDestroy() {
32+
document.querySelector('.panel-wm-content')!.classList.remove('dark-mode');
33+
document.querySelector<HTMLDivElement>('#demo-container')!.dataset.bsTheme = 'light';
34+
}
35+
36+
angularGridReady1(angularGrid: AngularGridInstance) {
37+
this.angularGrid1 = angularGrid;
38+
}
39+
40+
isBrowserDarkModeEnabled() {
41+
return window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
42+
}
43+
44+
prepareGrid() {
2645
this.columnDefinitions1 = [
2746
{ id: 'title', name: 'Title', field: 'title', sortable: true },
2847
{ id: 'duration', name: 'Duration (days)', field: 'duration', sortable: true },
@@ -31,7 +50,9 @@ export class GridBasicComponent implements OnInit {
3150
{ id: 'finish', name: 'Finish', field: 'finish', formatter: Formatters.dateIso },
3251
{ id: 'effort-driven', name: 'Effort Driven', field: 'effortDriven', sortable: true }
3352
];
53+
this._darkModeGrid1 = this.isBrowserDarkModeEnabled();
3454
this.gridOptions1 = {
55+
darkMode: this._darkModeGrid1,
3556
enableAutoResize: false,
3657
enableSorting: true,
3758
gridHeight: 225,
@@ -44,6 +65,7 @@ export class GridBasicComponent implements OnInit {
4465
this.gridOptions2 = {
4566
...this.gridOptions1,
4667
...{
68+
darkMode: false,
4769
enablePagination: true,
4870
pagination: {
4971
pageSizes: [5, 10, 20, 25, 50],
@@ -79,4 +101,14 @@ export class GridBasicComponent implements OnInit {
79101

80102
return mockDataset;
81103
}
104+
105+
toggleDarkModeGrid1() {
106+
this._darkModeGrid1 = !this._darkModeGrid1;
107+
if (this._darkModeGrid1) {
108+
document.querySelector('.grid-container1')?.classList.add('dark-mode');
109+
} else {
110+
document.querySelector('.grid-container1')?.classList.remove('dark-mode');
111+
}
112+
this.angularGrid1.slickGrid?.setOptions({ darkMode: this._darkModeGrid1 });
113+
}
82114
}

src/app/examples/grid-composite-editor.component.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<div id="demo-container" class="container-fluid">
22
<h2>
33
{{title}}
4+
<button class="btn btn-outline-secondary btn-sm ms-3" (click)="toggleDarkModeGrid()"
5+
data-test="toggle-dark-mode">
6+
<span>Toggle Dark Mode</span>
7+
</button>
48
<span class="float-end">
59
<a style="font-size: 18px"
610
target="_blank"
@@ -61,7 +65,6 @@ <h2>
6165
</div>
6266
</div>
6367

64-
6568
<angular-slickgrid gridId="grid30"
6669
[columnDefinitions]="columnDefinitions"
6770
[gridOptions]="gridOptions"
Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
$slick-button-border-color: #ababab !default;
22

33
.editable-field {
4+
// box-shadow: inset 0 0 0 1px lightblue !important;
45
background-color: rgba(227, 240, 251, 0.569) !important;
56
}
7+
.slick-dark-mode .editable-field {
8+
background-color: rgb(105 123 145 / 57%) !important
9+
}
610
.unsaved-editable-field {
711
background-color: #fbfdd1 !important;
812
}
9-
.button-style {
10-
cursor: pointer;
11-
background-color: white;
12-
border: 1px solid #{$slick-button-border-color};
13-
border-radius: 2px;
14-
justify-content: center;
15-
text-align: center;
16-
&:hover {
17-
border-color: darken($slick-button-border-color, 10%);
18-
}
13+
.slick-dark-mode .unsaved-editable-field {
14+
background-color: rgba(255, 183, 50, 0.8) !important;
15+
color: white;
16+
}
17+
18+
.slick-dark-mode {
19+
--bs-btn-color: #bebebe;
20+
}
21+
.panel-wm {
22+
width: calc(100vw - 12px);
1923
}

src/app/examples/grid-composite-editor.component.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
1+
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
22
import { HttpClient } from '@angular/common/http';
33
import { ExcelExportService } from '@slickgrid-universal/excel-export';
44
import { SlickCompositeEditor, SlickCompositeEditorComponent } from '@slickgrid-universal/composite-editor-component';
@@ -36,7 +36,7 @@ const URL_COUNTRIES_COLLECTION = 'assets/data/countries.json';
3636
* @returns {boolean} isEditable
3737
*/
3838
function checkItemIsEditable(dataContext: any, columnDef: Column, grid: SlickGrid) {
39-
const gridOptions = grid && grid.getOptions && grid.getOptions();
39+
const gridOptions = grid?.getOptions();
4040
const hasEditor = columnDef.editor;
4141
const isGridEditable = gridOptions.editable;
4242
let isEditable = !!(isGridEditable && hasEditor);
@@ -83,7 +83,8 @@ const myCustomTitleValidator = (value: any, args: any) => {
8383
styleUrls: ['./grid-composite-editor.component.scss'],
8484
encapsulation: ViewEncapsulation.None,
8585
})
86-
export class GridCompositeEditorComponent implements OnInit {
86+
export class GridCompositeEditorComponent implements OnDestroy, OnInit {
87+
private _darkModeGrid = false;
8788
title = 'Example 30: Composite Editor Modal';
8889
subTitle = `Composite Editor allows you to Create, Clone, Edit, Mass Update & Mass Selection Changes inside a nice Modal Window.
8990
<br>The modal is simply populated by looping through your column definition list and also uses a lot of the same logic as inline editing (see <a href="https://ghiscoding.gitbook.io/angular-slickgrid/grid-functionalities/composite-editor-modal" target="_blank">Composite Editor - Uncyclo</a>.)`;
@@ -122,6 +123,11 @@ export class GridCompositeEditorComponent implements OnInit {
122123
this.dataset = this.loadData(NB_ITEMS);
123124
}
124125

126+
ngOnDestroy() {
127+
document.querySelector('.panel-wm-content')!.classList.remove('dark-mode');
128+
document.querySelector<HTMLDivElement>('#demo-container')!.dataset.bsTheme = 'light';
129+
}
130+
125131
prepareGrid() {
126132
this.columnDefinitions = [
127133
{
@@ -378,6 +384,7 @@ export class GridCompositeEditorComponent implements OnInit {
378384
container: '#demo-container',
379385
rightPadding: 10
380386
},
387+
darkMode: this._darkModeGrid,
381388
enableAutoSizeColumns: true,
382389
enableAutoResize: true,
383390
showCustomFooter: true,
@@ -615,6 +622,11 @@ export class GridCompositeEditorComponent implements OnInit {
615622
resetFormButtonIconCssClass: 'fa fa-undo',
616623
onClose: () => Promise.resolve(confirm('You have unsaved changes, are you sure you want to close this window?')),
617624
onError: (error) => alert(error.message),
625+
onRendered: (modalElm) => {
626+
// Bootstrap requires extra attribute when toggling Dark Mode (data-bs-theme="dark")
627+
// we need to manually add this attribute ourselve before opening the Composite Editor Modal
628+
modalElm.dataset.bsTheme = this._darkModeGrid ? 'dark' : 'light';
629+
},
618630
onSave: (formValues, _selection, dataContext) => {
619631
const serverResponseDelay = 50;
620632

@@ -750,6 +762,18 @@ export class GridCompositeEditorComponent implements OnInit {
750762
this.editQueue = [];
751763
}
752764

765+
toggleDarkModeGrid() {
766+
this._darkModeGrid = !this._darkModeGrid;
767+
if (this._darkModeGrid) {
768+
document.querySelector<HTMLDivElement>('.panel-wm-content')!.classList.add('dark-mode');
769+
document.querySelector<HTMLDivElement>('#demo-container')!.dataset.bsTheme = 'dark';
770+
} else {
771+
document.querySelector('.panel-wm-content')!.classList.remove('dark-mode');
772+
document.querySelector<HTMLDivElement>('#demo-container')!.dataset.bsTheme = 'light';
773+
}
774+
this.angularGrid.slickGrid?.setOptions({ darkMode: this._darkModeGrid });
775+
}
776+
753777
mockProducts() {
754778
return [
755779
{

src/app/examples/grid-contextmenu.component.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<div class="container-fluid">
22
<h2>
33
{{title}}
4+
<button class="btn btn-outline-secondary btn-sm ms-3" (click)="toggleDarkModeGrid()"
5+
data-test="toggle-dark-mode">
6+
<span>Toggle Dark Mode</span>
7+
</button>
48
<span class="float-end">
59
<a style="font-size: 18px"
610
target="_blank"

0 commit comments

Comments
 (0)