Skip to content

Commit

Permalink
feat: add Dark Mode option (#232)
Browse files Browse the repository at this point in the history
- add a `darkMode` option, user can also change it at any time dynamically via `refreshOptions()`
  • Loading branch information
ghiscoding authored Feb 24, 2024
1 parent 1f712f1 commit d0f20e2
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 17 deletions.
2 changes: 2 additions & 0 deletions packages/demo/src/app-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Example11 from './examples/example11';
import Example12 from './examples/example12';
import Example13 from './examples/example13';
import Example14 from './examples/example14';
import Example15 from './examples/example15';
import GettingStarted from './getting-started';
import I18n from './i18n/i18n';
import Methods01 from './methods/methods01';
Expand Down Expand Up @@ -87,6 +88,7 @@ export const exampleRouting = [
{ name: 'example12', view: '/src/examples/example12.html', viewModel: Example12, title: 'Checkbox/Radio Icons' },
{ name: 'example13', view: '/src/examples/example13.html', viewModel: Example13, title: 'Dynamically Create Select' },
{ name: 'example14', view: '/src/examples/example14.html', viewModel: Example14, title: 'The Divider' },
{ name: 'example15', view: '/src/examples/example15.html', viewModel: Example15, title: 'Dark Mode' },
],
},
{
Expand Down
136 changes: 136 additions & 0 deletions packages/demo/src/examples/example15.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<div class="example15-container">
<div class="row mb-2">
<div class="col-md-12 title-desc">
<h2 class="bd-title">
Dark Mode
<span class="float-end links">
Code <span class="fa fa-link"></span>
<span class="small">
<a
target="_blank"
href="https://github.com/ghiscoding/multiple-select-vanilla/blob/main/packages/demo/src/examples/example15.html"
>html</a
>
|
<a target="_blank" href="https://github.com/ghiscoding/multiple-select-vanilla/blob/main/packages/demo/src/examples/example15.ts"
>ts</a
>
</span>
</span>
</h2>
<div class="demo-subtitle">
Dark Mode requires <code>darkMode</code> option to be enabled, when that happens it will add <code>.ms-dark-mode</code> to the parent and drop elements.
The dark theme is configured through CSS variables which you can also customize yourself.
You can also toggle Dark Mode at any time dynamically via <code>refreshOptions()</code>
</div>
</div>
</div>

<div>
<div class="mb-3 row">
<label class="col-sm-2">Single Select</label>

<div class="col-sm-10">
<select id="single" class="select full-width ms-dark-mode" data-test="single">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option data-divider="true"></option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option data-divider="true"></option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2">Single Radio</label>

<div class="col-sm-10">
<select id="single" class="radio full-width ms-dark-mode" data-test="radio">
<option value="1">January</option>
<option value="2" selected>February</option>
<option value="3">March</option>
<option value="4">April</option>
<option data-divider="true"></option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option data-divider="true"></option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2">Multiple Select</label>

<div class="col-sm-10">
<select id="multiple" class="select full-width" data-test="multiple" multiple>
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option data-divider="true"></option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option data-divider="true"></option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2">Group Select</label>

<div class="col-sm-10">
<select id="group" class="select full-width" data-test="group" multiple>
<option data-divider="true"></option>
<optgroup label="Group 1">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option data-divider="true"></option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
</optgroup>
<option data-divider="true"></option>
<optgroup label="Group 2">
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option data-divider="true"></option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</optgroup>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2">Data Select 1</label>

<div class="col-sm-10">
<select id="data-select1" class="data-select full-width" data-test="data1" multiple></select>
</div>
</div>
</div>
</div>
7 changes: 7 additions & 0 deletions packages/demo/src/examples/example15.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.panel-wm-content.dark-mode {
background-color: #33393e;
h2.bd-title,
label {
color: #dddddd;
}
}
59 changes: 59 additions & 0 deletions packages/demo/src/examples/example15.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { type MultipleSelectInstance, multipleSelect } from 'multiple-select-vanilla';

import './example15.scss';

export default class Example {
ms1: MultipleSelectInstance[] = [];
ms2?: MultipleSelectInstance;
ms3?: MultipleSelectInstance;

mount() {
document.querySelector('.panel-wm-content')?.classList.add('dark-mode');

this.ms1 = multipleSelect('.select', { darkMode: true }) as MultipleSelectInstance[];
this.ms2 = multipleSelect('.radio', { darkMode: true, singleRadio: true }) as MultipleSelectInstance;
this.ms3 = multipleSelect('.data-select', {
darkMode: true,
dataTest: 'select1',
showOkButton: true,
data: [
{
value: 1,
text: 'Option 1',
},
{
value: 2,
text: 'Option 2',
},
{
value: 3,
text: 'Option 3',
},
{
divider: true,
},
{
value: 4,
text: 'Option 4',
},
{
value: 5,
text: 'Option 5',
},
{
value: 6,
text: 'Option 6',
},
],
}) as MultipleSelectInstance;
}

unmount() {
// destroy ms instance(s) to avoid DOM leaks
this.ms1.forEach(m => m.destroy());
this.ms2?.destroy();
this.ms3?.destroy();
this.ms1 = [];
document.querySelector('.panel-wm-content')?.classList.remove('dark-mode');
}
}
17 changes: 15 additions & 2 deletions packages/demo/src/methods/methods02.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,33 @@ <h2 class="bd-title">
<div class="mb-3 row">
<label class="col-sm-2"> Methods </label>

<div class="col-sm-10">
<div class="col-sm-5">
<button id="refreshOptions" class="btn btn-secondary">refreshOptions</button>
<button id="setDarkMode" class="btn btn-secondary">Toggle Dark Mode</button>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2"> Basic Select </label>

<div class="col-sm-10">
<select multiple="multiple" class="full-width">
<select multiple="multiple" class="full-width select1" data-test="select1">
<option value="text1">text1</option>
<option value="text2">text2</option>
<option value="text3">text3</option>
</select>
</div>
</div>

<div class="mb-3 row">
<label class="col-sm-2"> Multiple Select </label>

<div class="col-sm-10">
<select multiple="multiple" class="full-width select2" data-test="select2">
<option value="task1">Task 1</option>
<option value="task2">Task 2</option>
<option value="task3">Task 3</option>
</select>
</div>
</div>
</div>
27 changes: 19 additions & 8 deletions packages/demo/src/methods/methods02.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,33 @@ import { type MultipleSelectInstance, multipleSelect } from 'multiple-select-van

export default class Example {
ms1?: MultipleSelectInstance;
ms2?: MultipleSelectInstance;
darkMode2 = false;

mount() {
this.ms1 = multipleSelect('select', {
filter: true,
}) as MultipleSelectInstance;
this.ms1 = multipleSelect('.select1', { filter: true }) as MultipleSelectInstance;
this.ms2 = multipleSelect('.select2', { darkMode: this.darkMode2, showOkButton: true }) as MultipleSelectInstance;

document.querySelector('#refreshOptions')!.addEventListener('click', () => {
this.ms1?.refreshOptions({
filter: false,
});
});
document.querySelector('#refreshOptions')?.addEventListener('click', () => this.refreshOption1());
document.querySelector('#setDarkMode')?.addEventListener('click', () => this.toggleDarkMode2());
}

refreshOption1() {
this.ms1?.refreshOptions({ filter: false });
}

toggleDarkMode2() {
this.darkMode2 = !this.darkMode2;
this.ms2?.refreshOptions({ darkMode: this.darkMode2 });
}

unmount() {
// destroy ms instance(s) to avoid DOM leaks
this.ms1?.destroy();
this.ms2?.destroy();
this.ms1 = undefined;
this.ms2 = undefined;
document.querySelector('#refreshOptions')?.removeEventListener('click', () => this.refreshOption1());
document.querySelector('#setDarkMode')?.removeEventListener('click', () => this.toggleDarkMode2());
}
}
13 changes: 12 additions & 1 deletion packages/multiple-select-vanilla/src/MultipleSelectInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ export class MultipleSelectInstance {
dataset: { test: 'sel' },
});

if (this.options.darkMode) {
this.parentElm.classList.add('ms-dark-mode');
}

// add tooltip title only when provided
const parentTitle = this.elm.getAttribute('title') || '';
if (parentTitle) {
Expand All @@ -205,6 +209,10 @@ export class MultipleSelectInstance {
// default position is bottom
this.dropElm = createDomElement('div', { className: `ms-drop ${this.options.position}`, ariaExpanded: 'false' }, this.parentElm);

if (this.options.darkMode) {
this.dropElm.classList.add('ms-dark-mode');
}

// add data-name attribute when name option is defined
if (name) {
this.dropElm.dataset.name = name;
Expand Down Expand Up @@ -635,7 +643,6 @@ export class MultipleSelectInstance {
const liBlock: HtmlStruct = {
tagName: 'li',
props: {
className: liClasses,
role: 'option',
title,
ariaSelected: String(!!dataRow.selected),
Expand All @@ -644,6 +651,10 @@ export class MultipleSelectInstance {
children: [{ tagName: 'label', props: { className: labelClasses }, children: [inputBlock, spanLabelBlock] }],
};

if (liClasses) {
liBlock.props.className = liClasses;
}

const customStyleRules = this.options.cssStyler(dataRow);
const customStylerStr = String(this.options.styler(dataRow) || ''); // deprecated
if (customStylerStr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export interface MultipleSelectOption extends MultipleSelectLocale {
/** Use optional string to override selected count text "# of % selected" instead of `formatCountSelected()`, the latter should be preferred */
countSelectedText?: string;

/** Dark Mode will add `.ms-dark-mode` CSS class to drop and parent elements */
darkMode?: boolean;

/** provide custom data */
data?: { [value: string]: number | string | boolean } | Array<number | string | boolean | OptionRowData | OptionRowDivider | OptGroupRowData>;

Expand Down
24 changes: 24 additions & 0 deletions packages/multiple-select-vanilla/src/styles/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ $ms-drop-list-item-level1-padding-left: 28px !default;
$ms-drop-list-item-padding: 0.25rem 8px !default;
$ms-drop-list-item-disabled-filter: Alpha(Opacity = 35) !default;
$ms-drop-list-item-disabled-opacity: 0.35 !default;
$ms-drop-scrollbar-color: #c1c1c1 #f1f1f1 !default;
$ms-drop-zindex: 1050 !default;
$ms-input-focus-outline: none !default;
$ms-infinite-empty-option-height: 20px !default;
Expand Down Expand Up @@ -81,5 +82,28 @@ $ms-select-all-label-padding: 4px !default;
$ms-select-all-label-span-padding: 0 0 0 20px !default;
$ms-select-all-line-height: 18px !default;
$ms-select-all-padding: 4px !default;
$ms-select-all-text-font-weight: normal !default;
$ms-select-all-text-color: darken($primary-color, 5%) !default;
$ms-select-all-text-hover-color: transparent !default;

.ms-dark-mode {
--ms-checkbox-color: #{lighten($primary-color, 5%)};
--ms-choice-border: 1px solid #757575;
--ms-choice-bgcolor: #262b2f;
--ms-choice-color: #d4d4d4;
--ms-drop-background: #2a2f34;
--ms-drop-border: 1px solid #585858;
--ms-drop-color: #cccccc;
--ms-drop-hide-radio-hover-bgcolor: #{darken($primary-color, 5%)};
--ms-drop-option-divider-border-top: 1px solid #696969;
--ms-drop-scrollbar-color: #828282 #424242;
--ms-option-highlight-bg-color: #{darken($primary-color, 10%)};
--ms-ok-button-bg-color: #262b2f;
--ms-ok-button-bg-hover-color: #24282c;
--ms-ok-button-border-color: #4a4a4a;
--ms-ok-button-text-color: #{lighten($primary-color, 5%)};
--ms-ok-button-text-hover-color: #{lighten($primary-color, 5%)};
--ms-select-all-border-bottom: 1px solid #5d5d5d;
--ms-select-all-text-color: #d4d4d4;
--ms-select-all-text-font-weight: bold;
}
Loading

0 comments on commit d0f20e2

Please sign in to comment.