ES6: port datepicker and util to ES6
Change-Id: I203aa47229e6c4dacd3a33bea39c512f4576a182
diff --git a/dev/js/src-lib/datepicker/datepicker.js b/dev/js/src-lib/datepicker/datepicker.js
new file mode 100644
index 0000000..859bd75
--- /dev/null
+++ b/dev/js/src-lib/datepicker/datepicker.js
@@ -0,0 +1,543 @@
+/**
+ * Date picker for the
+ * Virtual Collection builder.
+ *
+ * @author Nils Diewald
+ */
+
+import KorAP from '../util/util.js';
+
+const validDateMatchRE = new RegExp("^(?:[lg]?eq|ne)$");
+const validDateRE = new RegExp("^(?:\\d{4})(?:-\\d\\d(?:-\\d\\d)?)?$");
+
+KorAP._validDateMatchRE = validDateMatchRE;
+KorAP._validDateRE = validDateRE;
+
+/*
+ * Localizations
+ */
+const loc = KorAP.Locale;
+loc.WDAY = loc.WDAY || [
+ 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'
+];
+loc.MONTH = loc.MONTH || [
+ 'January', 'February', 'March', 'April',
+ 'May', 'June', 'July', 'August',
+ 'September', 'October', 'November',
+ 'December'
+];
+
+const d = document;
+
+// The datepicker class
+class KalamarDatepicker {
+
+ // Init datepicker
+ constructor() {
+ this._selected = [];
+ }
+
+
+ /**
+ * Get or select a specific date.
+ */
+ select(year, month, day) {
+ const t = this;
+ if (arguments.length >= 1) {
+ t._selected = {'year' : year};
+ t._showYear = year;
+ if (arguments.length >= 2) {
+ t._selected['month'] = month;
+ t._showMonth = month;
+ if (arguments.length >= 3) {
+ t._selected['day'] = day;
+ t._showDay = day;
+ };
+ };
+
+ return t;
+ };
+
+ return t._selected;
+ }
+
+
+ /**
+ * Select a specific date and
+ * init the accompanied action.
+ */
+ set(year, month, day) {
+ this.select(year, month, day);
+ this._store();
+ }
+
+
+ // Store the selected value
+ _store() {
+ if (this._click !== undefined)
+ this._click(this._selected);
+ else
+ console.dir(this._selected);
+ }
+
+
+ /**
+ * Set the action for clicking as a callback.
+ * The callback will retrieve a an object with
+ * an optional year attribute,
+ * an optional month attribute,
+ * and an optional day attribute
+ */
+ onclick(cb) {
+ this._click = cb;
+ }
+
+
+ /**
+ * The associated input field.
+ */
+ input() {
+ return this._input;
+ }
+
+
+ /**
+ * Show the datepicker.
+ * Will either show the selected year/month
+ * or the current date.
+ * Will return the element for appending to the dom.
+ */
+ show(year, month) {
+
+ const e = this._el = d.createElement('div');
+ e.setAttribute('tabindex', 0);
+ e.style.outline = 0;
+ e.classList.add('datepicker');
+
+ const today = new Date();
+ const t = this;
+
+ // Show year
+ t._showYear = (year !== undefined) ? year :
+ (t._selected['year'] ? this._selected['year'] :
+ today.getYear() + 1900);
+
+ // Show month
+ t._showMonth = month ? month :
+ (t._selected['month'] ? t._selected['month'] :
+ (today.getMonth() + 1));
+
+ // Append all helpers
+ e.appendChild(t._monthHelper());
+ e.appendChild(t._yearHelper());
+ e.appendChild(t._dayHelper());
+ t._input = e.appendChild(t._stringHelper());
+
+ // Always focus
+ e.addEventListener(
+ 'mousedown',
+ function (ev) {
+ this._inField = true
+ }.bind(t)
+ );
+
+ e.addEventListener(
+ 'mouseup',
+ function (ev) {
+ this._inField = false;
+ this._input.focus();
+ }.bind(t)
+ );
+
+ t._input.addEventListener(
+ 'blur',
+ function (ev) {
+ if (!this._inField) {
+ if (this.fromString(this._input.value)) {
+ this._store();
+ };
+ };
+ ev.halt();
+ }.bind(t)
+ );
+
+ t._input.focus();
+
+ return t._el;
+ }
+
+ _stringHelper() {
+
+ // Create element
+ // Add input field
+ const input = d.createElement('input');
+ input.value = this.toString();
+ input.setAttribute('tabindex', 0);
+
+ input.addEventListener(
+ 'keyup',
+ function (e) {
+ if (this.fromString(input.value)) {
+ this._updateYear();
+ this._updateMonth();
+ this._updateDay();
+ };
+ }.bind(this)
+ );
+
+ input.addEventListener(
+ 'keypress',
+ function (e) {
+ if (e.keyCode == 13) {
+ if (this.fromString(input.value))
+ this._store();
+
+ e.halt();
+ return false;
+ }
+ }.bind(this)
+ )
+
+ return input;
+ }
+
+
+ /**
+ * Get the HTML element associated with the datepicker.
+ */
+ element() {
+ return this._el;
+ }
+
+
+ /**
+ * Get the current date in string format.
+ */
+ today() {
+ const today = new Date();
+ let str = today.getYear() + 1900;
+ const m = today.getMonth() + 1;
+ const d = today.getDate();
+ str += '-' + (m < 10 ? '0' + m : m);
+ str += '-' + (d < 10 ? '0' + d : d);
+ return str;
+ }
+
+
+ /**
+ * Stringification
+ */
+ toString() {
+ // There are values selected
+ let v = '';
+ const s = this._selected;
+ if (s['year']) {
+ v += s['year'];
+ if (s['month']) {
+ v += '-';
+ v += s['month'] < 10 ? '0' + s['month'] : s['month'];
+ if (s['day']) {
+ v += '-';
+ v += s['day'] < 10 ? '0' + s['day'] : s['day'];
+ };
+ };
+ };
+ return v;
+ }
+
+
+ /**
+ * Increment the year.
+ */
+ incrYear() {
+ const t = this;
+ if (t._showYear < 9999) {
+ t._showYear++;
+ t._updateYear();
+ t._updateMonth();
+ t._updateDay();
+ return t;
+ };
+ return;
+ }
+
+
+ /**
+ * Decrement the year.
+ */
+ decrYear() {
+ const t = this;
+ if (t._showYear > 0) {
+ t._showYear--;
+ t._updateYear();
+ t._updateMonth();
+ t._updateDay();
+ return t;
+ };
+ return;
+ }
+
+
+ /**
+ * Increment the month.
+ */
+ incrMonth() {
+ const t = this;
+ t._showMonth++;
+ if (t._showMonth > 12) {
+ t._showMonth = 1;
+ t.incrYear();
+ }
+ else {
+ t._updateMonth();
+ t._updateDay();
+ };
+ return t;
+ }
+
+
+ /**
+ * Decrement the month.
+ */
+ decrMonth() {
+ const t = this;
+ t._showMonth--;
+ if (t._showMonth < 1) {
+ t._showMonth = 12;
+ t.decrYear();
+ }
+ else {
+ t._updateMonth();
+ t._updateDay();
+ };
+
+ return t;
+ }
+
+
+ // Create the year helper element.
+ _yearHelper() {
+ const t = this;
+ const year = d.createElement('div');
+ year.classList.add('year');
+
+ // Decrement year
+ year.addE('span')
+ .onclick = t.decrYear.bind(t);
+
+ t._yElement = year.addE('span');
+ t._yElement.addT(t._showYear);
+
+ t._yElement.onclick = function () {
+ t.set(t._showYear);
+ }.bind(t);
+ t._selectYear();
+
+ // Increment year
+ year.addE('span')
+ .onclick = t.incrYear.bind(t);
+
+ return year;
+ }
+
+
+ // Update the year helper view.
+ _updateYear() {
+ this._yElement.firstChild.data = this._showYear;
+ this._selectYear();
+ }
+
+
+ // Check if the viewed year is current
+ _selectYear() {
+ if (this._showYear === this.select()['year'])
+ this._yElement.classList.add('selected');
+ else
+ this._yElement.classList.remove('selected');
+ }
+
+
+ // Create the month helper element.
+ _monthHelper() {
+ const t = this;
+ const month = d.createElement('div');
+ month.classList.add('month');
+
+ // Decrement month
+ month.addE('span')
+ .onclick = t.decrMonth.bind(t);
+
+ t._mElement = month.addE('span');
+ t._mElement.addT(loc.MONTH[t._showMonth-1]);
+ t._mElement.onclick = function () {
+ this.set(this._showYear, this._showMonth);
+ }.bind(t);
+
+ t._selectMonth();
+
+ // Increment month
+ month.addE('span')
+ .onclick = t.incrMonth.bind(t);
+
+ return month;
+ }
+
+ // Update the month helper view.
+ _updateMonth() {
+ if (this._showMonth === undefined || this._showMonth > 12)
+ this._showMonth = 1;
+
+ this._mElement.firstChild.data = loc.MONTH[this._showMonth-1];
+ this._selectMonth();
+ }
+
+
+ // Check if the viewed month is current
+ _selectMonth() {
+ const t = this;
+ if (t._showYear === t.select()['year'] &&
+ t._showMonth === t.select()['month'])
+ t._mElement.classList.add('selected');
+ else
+ t._mElement.classList.remove('selected');
+ }
+
+
+ // Create the day (calendar) helper element.
+ _dayHelper() {
+ const table = d.createElement('table');
+
+ // Localized day view
+ const tr = table.addE('thead').addE('tr');
+ for (let i = 0; i < 7; i++) {
+ tr.addE('th').addT(loc.WDAY[i]);
+ };
+
+ this._dBElement = this._dayBody();
+
+ table.appendChild(this._dBElement);
+ return table;
+ }
+
+
+ // Create day body for calendar table
+ _dayBody() {
+ const showDate = new Date(
+ this._showYear,
+ this._showMonth - 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ const date = new Date(
+ this._showYear,
+ this._showMonth - 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ const today = new Date();
+ const that = this;
+
+ // What happens, in case someone clicks
+ // on a date
+ const click = function () {
+ that.set(
+ that._showYear,
+ that._showMonth,
+ parseInt(this.firstChild.data)
+ );
+ };
+
+ // Skip back to the previous monday (may be in the last month)
+ date.setDate(date.getDate() - ((date.getDay() + 6) % 7));
+
+ const tb = d.createElement('tbody');
+
+ const s = this.select();
+
+ let tr, i, td;
+
+ // Iterate over all days of the table
+ while (1) {
+
+ // Loop through the week
+ tr = tb.addE('tr');
+ for (i = 0; i < 7; i++) {
+ td = tr.addE('td');
+
+ // Not part of the current month
+ if (date.getMonth() !== showDate.getMonth()) {
+ td.classList.add('out');
+ }
+ else {
+ td.onclick = click;
+ };
+
+ // This is the current day
+ if (date.getDate() === today.getDate() &&
+ date.getMonth() === today.getMonth() &&
+ date.getFullYear() === today.getFullYear()) {
+ td.classList.add('today');
+ };
+
+ // This is the day selected
+ if (s && s['day']) {
+ if (date.getDate() === s['day'] &&
+ date.getMonth() === s['month']-1 &&
+ date.getFullYear() === s['year']) {
+ td.classList.add('selected');
+ };
+ };
+
+ // Add the current day to the table
+ td.addT(date.getDate());
+
+ // Next day
+ date.setDate(date.getDate() + 1);
+ };
+
+ if (date.getMonth() !== showDate.getMonth())
+ break;
+ };
+ return tb;
+ }
+
+ // Update the calendar view
+ _updateDay() {
+ const newBody = this._dayBody();
+ this._dBElement.parentNode.replaceChild(
+ newBody,
+ this._dBElement
+ );
+ this._dBElement = newBody;
+ }
+
+
+ /**
+ * Parse date from string.
+ */
+ fromString(v) {
+ if (v === undefined)
+ return false;
+
+ if (!KorAP._validDateRE.test(v))
+ return false;
+
+ const d = v.split('-', 3);
+ d[0] = parseInt(d[0]);
+ if (d[1]) d[1] = parseInt(d[1]);
+ if (d[2]) d[2] = parseInt(d[2]);
+
+ // Select values
+ this.select(d[0], d[1], d[2]);
+ return true;
+ }
+}
+
+export default KalamarDatepicker;
+export { validDateMatchRE, validDateRE };
diff --git a/dev/js/src-lib/datepicker/demo/datepickerdemo.js b/dev/js/src-lib/datepicker/demo/datepickerdemo.js
new file mode 100644
index 0000000..eeafd6f
--- /dev/null
+++ b/dev/js/src-lib/datepicker/demo/datepickerdemo.js
@@ -0,0 +1,18 @@
+import KalamarDatepicker from '../datepicker.js';
+import '../../../../scss/kalamar.scss';
+
+function mountDatepicker() {
+ const host = document.getElementById('dp');
+ if (!host) {
+ return;
+ }
+
+ const dp = new KalamarDatepicker();
+ host.appendChild(dp.select(2015, 4, 12).show(2015, 4));
+}
+
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', mountDatepicker);
+} else {
+ mountDatepicker();
+}
diff --git a/dev/js/src-lib/datepicker/demo/index.html b/dev/js/src-lib/datepicker/demo/index.html
new file mode 100644
index 0000000..0ac7e0c
--- /dev/null
+++ b/dev/js/src-lib/datepicker/demo/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Demo: Datepicker</title>
+</head>
+<body>
+ <h1>Datepicker</h1>
+ <script type="module" src="./datepickerdemo.js"></script>
+ <div class="vc">
+ <div id="dp"></div>
+ </div>
+</body>
+</html>
diff --git a/dev/js/src-lib/datepicker/package.json b/dev/js/src-lib/datepicker/package.json
new file mode 100644
index 0000000..e74fb87
--- /dev/null
+++ b/dev/js/src-lib/datepicker/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "kalamar-datepicker",
+ "version": "0.0.1",
+ "description": "Datepicker for Kalmar",
+ "main": "datepicker.js",
+ "scripts": {
+ "dev": "vite --open /demo/index.html"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/dev/js/src-lib/datepicker/test/datepickerSpec.js b/dev/js/src-lib/datepicker/test/datepickerSpec.js
new file mode 100644
index 0000000..711fd45
--- /dev/null
+++ b/dev/js/src-lib/datepicker/test/datepickerSpec.js
@@ -0,0 +1,176 @@
+import { describe, it, expect, beforeAll } from 'vitest';
+let KalamarDatepicker;
+
+describe('KorAP.Datepicker', function () {
+
+ beforeAll(async () => {
+ const module = await import('../datepicker.js');
+ KalamarDatepicker = module.default;
+ });
+
+ it('should be initializable', function () {
+ var dp = new KalamarDatepicker();
+ var e = dp.show();
+ expect(e.nodeName).toEqual('DIV');
+ expect(e.classList.contains('datepicker')).toBeTruthy();
+ expect(e.getAttribute('tabindex')).toEqual('0');
+ });
+
+ it('should generate valid dates', function () {
+ var dp = new KalamarDatepicker();
+ expect(dp.today()).toMatch(/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/);
+ });
+
+ it('should have year and month helpers', function () {
+ var dp = new KalamarDatepicker();
+ var e = dp.show(2013, 2);
+ expect(e.nodeName).toEqual('DIV');
+ expect(e.classList.contains('datepicker')).toBeTruthy();
+ expect(e.getAttribute('tabindex')).toEqual('0');
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2013');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+ });
+
+ it('should have modifyable year', function () {
+ var dp = new KalamarDatepicker();
+ var e = dp.show(2013, 2);
+ expect(e.nodeName).toEqual('DIV');
+ expect(e.classList.contains('datepicker')).toBeTruthy();
+ expect(e.getAttribute('tabindex')).toEqual('0');
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2013');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.incrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2014');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.incrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2015');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.decrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2014');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ // Max value
+ e = dp.show(9998, 2);
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('9998');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.incrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('9999');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.incrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('9999');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ // Min value
+ e = dp.show(2, 2);
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.decrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('1');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.decrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('0');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.decrYear();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('0');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+ });
+
+ it('should have modifyable month', function () {
+ var dp = new KalamarDatepicker();
+ var e = dp.show(2012, 9);
+
+ expect(e.nodeName).toEqual('DIV');
+ expect(e.classList.contains('datepicker')).toBeTruthy();
+
+ expect(e.getAttribute('tabindex')).toEqual('0');
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2012');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('September');
+
+ dp.incrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2012');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('October');
+
+ dp.incrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2012');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('November');
+
+ dp.incrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2012');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('December');
+
+ dp.incrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2013');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('January');
+
+ dp.decrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2012');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('December');
+
+ // Max value
+ e = dp.show(9999, 12);
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('9999');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('December');
+
+ dp.incrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('9999');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('December');
+
+ // Min value
+ e = dp.show(1, 2);
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('1');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.decrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('1');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('January');
+
+ dp.decrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('0');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('December');
+
+ e = dp.show(0, 2);
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('0');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('February');
+
+ dp.decrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('0');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('January');
+
+ dp.decrMonth();
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('0');
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('January');
+
+ });
+
+ it('should parse from string', function () {
+ var dp = new KalamarDatepicker();
+ expect(dp.fromString()).toBeFalsy();
+ expect(dp.fromString("2020-September")).toBeFalsy();
+ expect(dp.fromString("2020")).toBeTruthy();
+ expect(dp.fromString("2020-10")).toBeTruthy();
+ expect(dp.fromString("2020-10-9")).toBeFalsy();
+ expect(dp.fromString("2020-10-09")).toBeTruthy();
+
+ expect(dp._selected['year']).toEqual(2020);
+ expect(dp._selected['month']).toEqual(10);
+ expect(dp._selected['day']).toEqual(9);
+
+ var e = dp.show(2020, 11);
+
+ expect(e.querySelector('div.year > span:nth-child(2)').firstChild.data).toEqual('2020');
+ expect(e.querySelector('div.year > span:nth-child(2)').classList.contains('selected')).toBeTruthy();
+ expect(e.querySelector('div.month > span:nth-child(2)').firstChild.data).toEqual('November');
+ expect(e.querySelector('div.month > span:nth-child(2)').classList.contains('selected')).toBeFalsy();
+ expect(e.querySelector('div.month > span:nth-child(2)').classList.contains('selected')).toBeFalsy();
+
+ expect(dp.toString()).toEqual("2020-10-09");
+ });
+});
diff --git a/dev/js/src-lib/datepicker/vite.config.js b/dev/js/src-lib/datepicker/vite.config.js
new file mode 100644
index 0000000..1edc2f0
--- /dev/null
+++ b/dev/js/src-lib/datepicker/vite.config.js
@@ -0,0 +1,29 @@
+import { defineConfig } from 'vite';
+import path from 'path';
+
+const scssDir = path.resolve(__dirname, '../../../scss'); // Path to your SCSS directory
+const fontDir = path.resolve(__dirname, '../../../font');
+
+export default defineConfig({
+
+ server: {
+ fs: {
+ allow: [
+ scssDir,
+ fontDir,
+ __dirname
+ ]
+ },
+ port: 3000
+ },
+
+
+ css: {
+ preprocessorOptions: {
+ scss: {
+ loadPaths: [scssDir]
+ }
+ }
+ }
+});
+