From 3cc3559348155f7e05b6c1c0da934910072dcf77 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Sun, 15 Dec 2024 21:01:59 +0100 Subject: [PATCH 01/48] accessibility(Autocomplete) implemented tabindex and aria expanded #538 --- src/autocomplete.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/autocomplete.ts b/src/autocomplete.ts index e07ee62335..944736b8b7 100644 --- a/src/autocomplete.ts +++ b/src/autocomplete.ts @@ -3,7 +3,7 @@ import { Dropdown, DropdownOptions } from "./dropdown"; import { Component, BaseOptions, InitElements, MElement } from "./component"; export interface AutocompleteData { - /** + /** * A primitive value that can be converted to string. * If "text" is not provided, it will also be used as "option text" as well */ @@ -82,7 +82,7 @@ let _defaults: AutocompleteOptions = { onSearch: (text: string, autocomplete: Autocomplete) => { const normSearch = text.toLocaleLowerCase(); autocomplete.setMenuItems( - autocomplete.options.data.filter((option) => + autocomplete.options.data.filter((option) => option.id.toString().toLocaleLowerCase().includes(normSearch) || option.text?.toLocaleLowerCase().includes(normSearch) ) @@ -118,7 +118,7 @@ export class Autocomplete extends Component { ...Autocomplete.defaults, ...options }; - + this.isOpen = false; this.count = 0; this.activeIndex = -1; @@ -215,6 +215,7 @@ export class Autocomplete extends Component { this.container.style.maxHeight = this.options.maxDropDownHeight; this.container.id = `autocomplete-options-${Utils.guid()}`; this.container.classList.add('autocomplete-content', 'dropdown-content'); + this.container.ariaExpanded = 'true'; this.el.setAttribute('data-target', this.container.id); this.menuItems.forEach(menuItem => { @@ -260,6 +261,7 @@ export class Autocomplete extends Component { } _removeDropdown() { + this.container.ariaExpanded = 'false'; this.container.parentNode.removeChild(this.container); } @@ -368,6 +370,7 @@ export class Autocomplete extends Component { 'style', 'display:grid; grid-auto-flow: column; user-select: none; align-items: center;' ); + item.tabIndex = 0; // Checkbox if (this.options.isMultiSelect) { item.innerHTML = ` From 535105125c56739bb8cdc2af88932c91a8b3682c Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Sun, 15 Dec 2024 20:05:16 +0100 Subject: [PATCH 02/48] accessibility(FloatingActionButton) implemented tabindex and aria expanded #540 --- src/buttons.ts | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/buttons.ts b/src/buttons.ts index 3d4a6a5853..cd79262146 100644 --- a/src/buttons.ts +++ b/src/buttons.ts @@ -1,4 +1,5 @@ import { Component, BaseOptions, InitElements, MElement, Openable } from "./component"; +import { Utils } from './utils'; export interface FloatingActionButtonOptions extends BaseOptions { /** @@ -59,6 +60,8 @@ export class FloatingActionButton extends Component this.offsetX = 0; this.el.classList.add(`direction-${this.options.direction}`); + this._anchor.tabIndex = 0; + this._menu.ariaExpanded = 'false'; if (this.options.direction === 'top') this.offsetY = 40; else if (this.options.direction === 'right') @@ -111,6 +114,7 @@ export class FloatingActionButton extends Component } else { this.el.addEventListener('click', this._handleFABClick); } + this.el.addEventListener('keypress', this._handleFABKeyPress); } _removeEventHandlers() { @@ -120,9 +124,20 @@ export class FloatingActionButton extends Component } else { this.el.removeEventListener('click', this._handleFABClick); } + this.el.removeEventListener('keypress', this._handleFABKeyPress); } _handleFABClick = () => { + this._handleFABToggle() +} + + _handleFABKeyPress = (e) => { + if(Utils.keys.ENTER.includes(e.key)) { + this._handleFABToggle(); + } + } + + _handleFABToggle = () => { if (this.isOpen) { this.close(); } else { @@ -164,9 +179,10 @@ export class FloatingActionButton extends Component _animateInFAB() { this.el.classList.add('active'); + this._menu.ariaExpanded = 'true'; const delayIncrement = 40; const duration = 275; - + this._floatingBtnsReverse.forEach((el, index) => { const delay = delayIncrement * index; el.style.transition = 'none'; @@ -181,6 +197,7 @@ export class FloatingActionButton extends Component el.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`; el.style.opacity = '1'; el.style.transform = 'translate(0, 0) scale(1)'; + el.tabIndex = 0; }, 1); }, delay); }); @@ -188,12 +205,16 @@ export class FloatingActionButton extends Component _animateOutFAB() { const duration = 175; - setTimeout(() => this.el.classList.remove('active'), duration); + setTimeout(() => { + this.el.classList.remove('active'), duration; + this._menu.ariaExpanded = 'false'; + }); this._floatingBtnsReverse.forEach((el) => { el.style.transition = `opacity ${duration}ms ease, transform ${duration}ms ease`; // to el.style.opacity = '0'; el.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(0.4)`; + el.tabIndex = -1; }); } @@ -225,6 +246,7 @@ export class FloatingActionButton extends Component this.el.style.left = '0'; this.el.style.transform = 'translateX(' + this.offsetX + 'px)'; this.el.style.transition = 'none'; + this._menu.ariaExpanded = 'true'; this._anchor.style.transform = `translateY(${this.offsetY}px`; this._anchor.style.transition = 'none'; @@ -246,7 +268,10 @@ export class FloatingActionButton extends Component backdrop.style.transform = 'scale(' + scaleFactor + ')'; backdrop.style.transition = 'transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)'; - this._menu.querySelectorAll('li > a').forEach((a: HTMLAnchorElement) => a.style.opacity = '1'); + this._menu.querySelectorAll('li > a').forEach((a: HTMLAnchorElement) => { + a.style.opacity = '1'; + a.tabIndex = 0; + }); // Scroll to close. window.addEventListener('scroll', this.close, true); From 2fbf95e9cc37df20cbadb2285744ba5560682415 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Thu, 26 Dec 2024 23:07:43 +0100 Subject: [PATCH 03/48] accessibility(Buttons) implemented focus state #81 --- sass/components/_buttons.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sass/components/_buttons.scss b/sass/components/_buttons.scss index 8c3cecc7ae..dbf1767a30 100644 --- a/sass/components/_buttons.scss +++ b/sass/components/_buttons.scss @@ -126,6 +126,10 @@ //------------------ Focus +.btn:focus { + background-color: var(--md-sys-color-primary-container); +} + .btn:focus.elevated { @extend .z-depth-1; color: var(--md-sys-color-primary); From 7c8fbac9cc2b4f0b84e2af4c2efca25e399470f8 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Thu, 26 Dec 2024 23:08:11 +0100 Subject: [PATCH 04/48] accessibility(Tabs) implemented focus state #81 --- sass/components/_tabs.scss | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/sass/components/_tabs.scss b/sass/components/_tabs.scss index a62890e595..aee2f454d1 100644 --- a/sass/components/_tabs.scss +++ b/sass/components/_tabs.scss @@ -37,6 +37,7 @@ &.tabs-fixed-width { display: flex; + .tab { flex-grow: 1; } @@ -44,7 +45,7 @@ position: relative; overflow-x: auto; - overflow-y: hidden; + overflow-y: hidden; width: 100%; background-color: var(--md-sys-color-surface); margin: 0 auto; @@ -55,14 +56,14 @@ list-style-type: none; display: inline-block; text-align: center; - line-height: 48px; + line-height: 48px; padding: 0; margin: 0; i.material-icons { position: relative; - top: 8px; - vertical-align: middle; + top: 8px; + vertical-align: middle; } span { @@ -85,15 +86,22 @@ background-color: rgba(var(--md-sys-color-primary-numeric), 0.06); } - &:focus, + &:focus { + background-color: var(--md-sys-color-primary-container); + } + &.active { background-color: rgba(var(--md-sys-color-primary-numeric), 0.18); + } + + &:focus, + &.active { outline: none; } color: var(--md-sys-color-on-surface-variant); display: flex; - flex-direction: column; + flex-direction: column; width: 100%; height: 100%; min-height: 48px; //needed for only-icons tabs @@ -109,6 +117,7 @@ &:not(:focus) { background-color: transparent; } + color: var(--md-sys-color-on-surface); cursor: default; background-color: transparent; @@ -123,19 +132,19 @@ will-change: left, right; border-radius: 3px 3px 0 0; } - - &.tabs-horizontal .tab { + + &.tabs-horizontal .tab { height: 48px; a { display: block; } - + i.material-icons { padding: 0 4px; - position: relative; - top: -2px; - vertical-align: middle; + position: relative; + top: -2px; + vertical-align: middle; } } } @@ -145,8 +154,10 @@ @media #{$medium-and-down} { .tabs { display: flex; + .tab { flex-grow: 1; + a { padding: 0 12px; } From 1dbde8463dbdcd227d9ae4051df73fed1225cdd5 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 04:20:49 +0100 Subject: [PATCH 05/48] config(Rollup) include autoprefixer and postcss; updated rollupconfig #568 --- package-lock.json | 266 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 2 + rollup.config.ts | 27 ++++- 3 files changed, 273 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 48c0df456b..1ad3e07aca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,10 +23,12 @@ "@rollup/plugin-typescript": "^11.1.6", "@types/node": "^20.5.2", "archiver": "^7.0.1", + "autoprefixer": "^9.8.6", "eslint": "^9.10.0", "jasmine": "^5.1.0", "jasmine-browser-runner": "^2.5.0", "jasmine-core": "^5.2.0", + "postcss": "^8.4.49", "rollup": "^4.20.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^6.1.1", @@ -1401,6 +1403,54 @@ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/autoprefixer/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/b4a": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", @@ -1503,6 +1553,39 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1573,6 +1656,27 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1902,6 +2006,13 @@ "node": ">=0.10.0" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.76", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", + "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", + "dev": true, + "license": "ISC" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -1935,6 +2046,16 @@ "node": ">= 0.4" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3360,6 +3481,25 @@ "dev": true, "license": "MIT" }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3375,6 +3515,13 @@ "node": ">= 0.6" } }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3384,6 +3531,23 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -3578,12 +3742,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3597,6 +3760,42 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4213,11 +4412,22 @@ "dev": true, "license": "MIT" }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -4232,15 +4442,6 @@ "source-map": "^0.6.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -4581,6 +4782,37 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index c53ca87e32..0c6724c035 100644 --- a/package.json +++ b/package.json @@ -57,10 +57,12 @@ "@rollup/plugin-typescript": "^11.1.6", "@types/node": "^20.5.2", "archiver": "^7.0.1", + "autoprefixer": "^9.8.6", "eslint": "^9.10.0", "jasmine": "^5.1.0", "jasmine-browser-runner": "^2.5.0", "jasmine-core": "^5.2.0", + "postcss": "^8.4.49", "rollup": "^4.20.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^6.1.1", diff --git a/rollup.config.ts b/rollup.config.ts index 5a13433de5..d5e3664f61 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -7,6 +7,8 @@ import scss from 'rollup-plugin-scss'; import copy from 'rollup-plugin-copy'; import { readFileSync } from 'fs'; +import autoprefixer from 'autoprefixer'; +import postcss from 'postcss'; const packageJson = JSON.parse(readFileSync('./package.json').toString()); const outputPath = 'dist/js/materialize'; @@ -102,12 +104,25 @@ const config: RollupOptions[] = [ //--- CSS { input: 'sass/materialize.scss', + output: [{ file: 'dist/css/materialize.css' }], // overwritten + plugins: [ + scss({ + fileName: 'materialize.css', + processor: css => postcss([autoprefixer]).process(css).then(result => result.css), + }) + ], + onwarn: (warning, defaultHandler) => { + if (!(warning.code === 'FILE_NAME_CONFLICT' || warning.code === 'EMPTY_BUNDLE')) + defaultHandler(warning); + } + }, + { + input: 'dist/css/materialize.css', output: [{ file: 'dist/css/materialize.min.css' }], // overwritten plugins: [ scss({ fileName: 'materialize.min.css', - outputStyle: 'compressed', - sourceMap: true + sourceMap: true, }) ], onwarn: (warning, defaultHandler) => { @@ -116,11 +131,13 @@ const config: RollupOptions[] = [ } }, { - input: 'sass/materialize.scss', - output: [{ file: 'dist/css/materialize.css' }], // overwritten + input: 'dist/css/materialize.css', + output: [{ file: 'dist/css/materialize.min.css' }], // overwritten plugins: [ scss({ - fileName: 'materialize.css' + fileName: 'materialize.min.css', + outputStyle: 'compressed', + processor: (css, map) => postcss([autoprefixer]).process(css, { from: 'materialize.min.css' }).then(result => result.css), }) ], onwarn: (warning, defaultHandler) => { From b5c797baa0ff9eba92547b1d32240023a400a35e Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 04:24:12 +0100 Subject: [PATCH 06/48] include environment build in npm release scripts and rollup configuration --- package.json | 4 ++-- rollup.config.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 0c6724c035..cefc336d0a 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ "lint": "eslint .", "test": "npm run build && jasmine-browser-runner runSpecs", "build": "rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript", - "release": "npm run lint && npm run build && node ci/compress.js", + "release": "npm run lint && npm run build -- --environment BUILD:release && node ci/compress.js", "preversion": "npm run lint && npm test", - "version": "npm run build && node ci/compress.js && git add ." + "version": "npm run build -- --environment BUILD:release && node ci/compress.js && git add ." }, "lint-staged": { "js/*.js": [ diff --git a/rollup.config.ts b/rollup.config.ts index d5e3664f61..5dd63e36e3 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -122,7 +122,7 @@ const config: RollupOptions[] = [ plugins: [ scss({ fileName: 'materialize.min.css', - sourceMap: true, + sourceMap: !(process.env.BUILD === 'release'), }) ], onwarn: (warning, defaultHandler) => { From 79b8121a68c4deb6c87fbe1737b1a9b216aac765 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 04:29:11 +0100 Subject: [PATCH 07/48] remove unused var --- rollup.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup.config.ts b/rollup.config.ts index 5dd63e36e3..919f22f6fc 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -137,7 +137,7 @@ const config: RollupOptions[] = [ scss({ fileName: 'materialize.min.css', outputStyle: 'compressed', - processor: (css, map) => postcss([autoprefixer]).process(css, { from: 'materialize.min.css' }).then(result => result.css), + processor: (css) => postcss([autoprefixer]).process(css, { from: 'materialize.min.css' }).then(result => result.css), }) ], onwarn: (warning, defaultHandler) => { From 5bd16fcefb47de277c9d925e203ed51eca3f4acc Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 04:59:02 +0100 Subject: [PATCH 08/48] config(Rollup) revert Rollupconfig to previous state and combine autoprefixing and sourcemapping into one function --- rollup.config.ts | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/rollup.config.ts b/rollup.config.ts index 919f22f6fc..f011f11f7f 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -21,6 +21,7 @@ const bannerText = `/*! * MIT License (https://raw.githubusercontent.com/materializecss/materialize/master/LICENSE) */`; +// @ts-ignore const config: RollupOptions[] = [ //--- Replace version in index.ts { @@ -104,25 +105,16 @@ const config: RollupOptions[] = [ //--- CSS { input: 'sass/materialize.scss', - output: [{ file: 'dist/css/materialize.css' }], // overwritten - plugins: [ - scss({ - fileName: 'materialize.css', - processor: css => postcss([autoprefixer]).process(css).then(result => result.css), - }) - ], - onwarn: (warning, defaultHandler) => { - if (!(warning.code === 'FILE_NAME_CONFLICT' || warning.code === 'EMPTY_BUNDLE')) - defaultHandler(warning); - } - }, - { - input: 'dist/css/materialize.css', output: [{ file: 'dist/css/materialize.min.css' }], // overwritten plugins: [ scss({ fileName: 'materialize.min.css', + outputStyle: 'compressed', sourceMap: !(process.env.BUILD === 'release'), + processor: (css, map) => ({ + css: postcss([autoprefixer]).process(css, { from: 'materialize.min.css' }).toString(), + map + }) }) ], onwarn: (warning, defaultHandler) => { @@ -131,12 +123,11 @@ const config: RollupOptions[] = [ } }, { - input: 'dist/css/materialize.css', - output: [{ file: 'dist/css/materialize.min.css' }], // overwritten + input: 'sass/materialize.scss', + output: [{ file: 'dist/css/materialize.css' }], // overwritten plugins: [ scss({ - fileName: 'materialize.min.css', - outputStyle: 'compressed', + fileName: 'materialize.css', processor: (css) => postcss([autoprefixer]).process(css, { from: 'materialize.min.css' }).then(result => result.css), }) ], From 801336563e101a2b936c7214514b8c1ac517a1d7 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 05:01:05 +0100 Subject: [PATCH 09/48] remove debug (ignore statement) --- rollup.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/rollup.config.ts b/rollup.config.ts index f011f11f7f..e728c625fd 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -21,7 +21,6 @@ const bannerText = `/*! * MIT License (https://raw.githubusercontent.com/materializecss/materialize/master/LICENSE) */`; -// @ts-ignore const config: RollupOptions[] = [ //--- Replace version in index.ts { From 7e1f83fa122d922c998402a1abb19b7f2be0db17 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 15:58:18 +0100 Subject: [PATCH 10/48] enhancement(Datepicker) refactor styling to accomply with material m3 standards #558; implemented styling for in range (wip) #360 --- sass/components/_datepicker.scss | 84 ++++++++++++++++++++++++-------- src/datepicker.ts | 13 +++-- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/sass/components/_datepicker.scss b/sass/components/_datepicker.scss index be6491e9c3..f276473b58 100644 --- a/sass/components/_datepicker.scss +++ b/sass/components/_datepicker.scss @@ -1,8 +1,9 @@ /* Modal */ .datepicker-modal { max-width: 325px; - min-width: 300px; - max-height: none; + // @removed since v2.2.1-dev regarding Material M3 standards + /* min-width: 300px; + max-height: none; */ } .datepicker-container.modal-content { @@ -75,9 +76,11 @@ /* Date Display */ .datepicker-date-display { flex: 1 auto; - background-color: var(--md-sys-color-primary); - color: var(--md-sys-color-on-primary); + // @removed since v2.2.1-dev regarding Material M3 standards + // background-color: var(--md-sys-color-primary); + // color: var(--md-sys-color-on-primary); padding: 20px 22px; + border-bottom: 1px solid var(--md-sys-color-surface-variant-light); font-weight: 500; .year-text { @@ -124,46 +127,88 @@ color: var(--md-sys-color-on-surface-variant); } - td { + .datepicker-day { color: var(--md-sys-color-on-background); &.is-today { color: var(--md-sys-color-primary); } - &.is-selected { + /*&.is-selected button { background-color: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); - } - - &.is-outside-current-month, - &.is-disabled { - color: var(--md-sys-color-on-surface); - pointer-events: none; - } + }*/ - border-radius: 50%; + // border-radius: 50%; padding: 0; } } +.datepicker-day.is-inrange { + .datepicker-day-container { + position: relative; + background-color: var(--md-sys-color-primary-container); + z-index: 0; + } + + // @todo find solution why pseudo selectors are not working or implement other method + &:first-child .datepicker-day-container:before, + &:last-child .datepicker-day-container:before { + position: absolute; + height: 100%; + width: 50%; + content: ''; + background-color: var(--md-sys-color-primary-container); + z-index: 0; + } + + &:first-child .datepicker-day-container:before { + left: -50%; + } + + &:last-child .datepicker-day-container:before { + left: auto; + right: -50%; + } +} + .datepicker-day-button { background-color: transparent; border: none; - line-height: 38px; + line-height: 34px; display: block; - width: 100%; + width: 34px; border-radius: 50%; + margin: 5px; padding: 0 5px; cursor: pointer; color: inherit; + position: relative; + z-index: 1; + &:hover { background-color: rgba(var(--md-sys-color-primary-numeric), 0.06); } &:focus { - background-color: rgba(var(--md-sys-color-primary-numeric), 0.18); + border-color: var(--md-sys-color-primary); + } + + .is-selected & { + background-color: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + + &:focus { + background-color: var(--md-sys-color-surface-variant); + color: var(--md-sys-color-primary); + } + } + + &.is-outside-current-month button, + &.is-disabled button { + color: var(--md-sys-color-on-surface); + pointer-events: none; } } @@ -192,7 +237,8 @@ /* Media Queries */ @media #{$medium-and-up} { - .datepicker-modal { + @removed since v2.2.1-dev regarding Material M3 standards + /*.datepicker-modal { max-width: 625px; } @@ -208,7 +254,7 @@ .datepicker-table, .datepicker-footer { width: 320px; - } + }*/ .datepicker-day-button { line-height: 44px; diff --git a/src/datepicker.ts b/src/datepicker.ts index a29c29204d..10190df938 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -804,7 +804,7 @@ export class Datepicker extends Component { } renderDay(opts) { - const arr = []; + const arr = ['datepicker-day']; let ariaSelected = 'false'; if (opts.isEmpty) { if (opts.showDaysInNextAndPreviousMonths) { @@ -834,9 +834,9 @@ export class Datepicker extends Component { arr.push('has-event'); } - // @todo should we create this additional css class? if (opts.isInRange) { arr.push('is-inrange'); + ariaSelected = 'true'; } // @todo should we create this additional css class? @@ -855,7 +855,9 @@ export class Datepicker extends Component { } return ( `` + + '
' + `` + + '
' + '' ); } @@ -955,7 +957,8 @@ export class Datepicker extends Component { ''; html += ``; + // @todo remove button class and add scss mixin, current implementation temporary for focus states, @see https://github.com/materializecss/materialize/issues/566 + } btn" type="button">${leftArrow}`; html += '
'; if (opts.showMonthAfterYear) { @@ -977,7 +980,8 @@ export class Datepicker extends Component { ''; html += ``; + // @todo remove button class and add scss mixin, current implementation temporary for focus states, @see https://github.com/materializecss/materialize/issues/566 + } btn" type="button">${rightArrow}`; return (html += '
'); } @@ -1035,6 +1039,7 @@ export class Datepicker extends Component { // Init Materialize Select const yearSelect = this.calendarEl.querySelector('.orig-select-year') as HTMLSelectElement; const monthSelect = this.calendarEl.querySelector('.orig-select-month') as HTMLSelectElement; + // @todo fix accessibility @see https://github.com/materializecss/materialize/issues/522 FormSelect.init(yearSelect, { classes: 'select-year', dropdownOptions: { container: document.body, constrainWidth: false } From a12be6193e23b7bb598b9e3e917ee32db70886c0 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 18:05:03 +0100 Subject: [PATCH 11/48] enhancement(Datepicker) refinement on daterange styling, revert day cell to initial, implemented additional is-daterange-start + is-daterange-end classes and refactored styling to use pseudo selectors on newly created classes #360 --- sass/components/_datepicker.scss | 37 ++++++++++++++++---------------- src/datepicker.ts | 16 +++++++++++--- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/sass/components/_datepicker.scss b/sass/components/_datepicker.scss index f276473b58..16a99d45a9 100644 --- a/sass/components/_datepicker.scss +++ b/sass/components/_datepicker.scss @@ -144,32 +144,33 @@ } } -.datepicker-day.is-inrange { - .datepicker-day-container { - position: relative; - background-color: var(--md-sys-color-primary-container); - z-index: 0; - } +.datepicker-day.is-daterange-start, +.datepicker-day.is-daterange-end, +.datepicker-day.is-daterange { + position: relative; - // @todo find solution why pseudo selectors are not working or implement other method - &:first-child .datepicker-day-container:before, - &:last-child .datepicker-day-container:before { + &:before { position: absolute; - height: 100%; - width: 50%; + top: 5px; + width: 100%; + height: 34px; content: ''; background-color: var(--md-sys-color-primary-container); z-index: 0; } +} - &:first-child .datepicker-day-container:before { - left: -50%; - } +.datepicker-day.is-daterange-start:before, +.datepicker-day.is-daterange-end:before { + width: 50%; +} - &:last-child .datepicker-day-container:before { - left: auto; - right: -50%; - } +.datepicker-day.is-daterange-start:before { + left: 50%; +} + +.datepicker-day.is-daterange .datepicker-day-button:before { + background-color: var(--md-sys-color-primary-container); } .datepicker-day-button { diff --git a/src/datepicker.ts b/src/datepicker.ts index 10190df938..b8d4ca7bad 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -739,6 +739,8 @@ export class Datepicker extends Component { (this.options.maxDate && day > this.options.maxDate) || (this.options.disableWeekends && Datepicker._isWeekend(day)) || (this.options.disableDayFn && this.options.disableDayFn(day)), + isDateRangeStart = this.options.isDateRange && Datepicker._compareDates(this.date, day), + isDateRangeEnd = this.options.isDateRange && Datepicker._compareDates(this.endDate, day), isDateRange = this.options.isDateRange && Datepicker._isDate(this.endDate) && @@ -788,6 +790,8 @@ export class Datepicker extends Component { isEndRange: isEndRange, isInRange: isInRange, showDaysInNextAndPreviousMonths: this.options.showDaysInNextAndPreviousMonths, + isDateRangeStart: isDateRangeStart, + isDateRangeEnd: isDateRangeEnd, isDateRange: isDateRange }; @@ -834,6 +838,7 @@ export class Datepicker extends Component { arr.push('has-event'); } + // @todo create additional css class if (opts.isInRange) { arr.push('is-inrange'); ariaSelected = 'true'; @@ -849,15 +854,20 @@ export class Datepicker extends Component { arr.push('is-endrange'); } - // @todo create additional css class + if (opts.isDateRangeStart) { + arr.push('is-daterange-start'); + } + + if (opts.isDateRangeEnd) { + arr.push('is-daterang-eend'); + } + if (opts.isDateRange) { arr.push('is-daterange'); } return ( `` + - '
' + `` + - '
' + '' ); } From b8c344b74c260209a1cf529a1de2fc3046276c4d Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 20:32:26 +0100 Subject: [PATCH 12/48] enhancement(Datepicker) refactor all modal related selectors #558 --- src/datepicker.ts | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/datepicker.ts b/src/datepicker.ts index b8d4ca7bad..46c747801b 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -260,7 +260,7 @@ export class Datepicker extends Component { doneBtn: HTMLElement; cancelBtn: HTMLElement; - modalEl: HTMLElement; + containerEl: HTMLElement; yearTextEl: HTMLElement; dateTextEl: HTMLElement; endDateEl: HTMLInputElement; @@ -409,7 +409,7 @@ export class Datepicker extends Component { destroy() { this._removeEventHandlers(); - this.modalEl.remove(); + this.containerEl.remove(); this.destroySelects(); this.el['M_Datepicker'] = undefined; } @@ -447,10 +447,11 @@ export class Datepicker extends Component { const optEl = this.options.container; this.options.container = optEl instanceof HTMLElement ? optEl : (document.querySelector(optEl) as HTMLElement); - this.options.container.append(this.modalEl); + this.options.container.append(this.containerEl); } else { - //this.modalEl.before(this.el); - this.el.parentElement.appendChild(this.modalEl); + //this.containerEl.before(this.el); + const appendTo = !this.endDateEl ? this.el : this.endDateEl; + appendTo.parentElement.after(this.containerEl); } } @@ -859,7 +860,7 @@ export class Datepicker extends Component { } if (opts.isDateRangeEnd) { - arr.push('is-daterang-eend'); + arr.push('is-daterange-end'); } if (opts.isDateRange) { @@ -1085,17 +1086,17 @@ export class Datepicker extends Component { const template = document.createElement('template'); template.innerHTML = Datepicker._template.trim(); - this.modalEl = template.content.firstChild; + this.containerEl = template.content.firstChild; - this.calendarEl = this.modalEl.querySelector('.datepicker-calendar'); - this.yearTextEl = this.modalEl.querySelector('.year-text'); - this.dateTextEl = this.modalEl.querySelector('.date-text'); + this.calendarEl = this.containerEl.querySelector('.datepicker-calendar'); + this.yearTextEl = this.containerEl.querySelector('.year-text'); + this.dateTextEl = this.containerEl.querySelector('.date-text'); if (this.options.showClearBtn) { - this.clearBtn = this.modalEl.querySelector('.datepicker-clear'); + this.clearBtn = this.containerEl.querySelector('.datepicker-clear'); } // TODO: This should not be part of the datepicker - this.doneBtn = this.modalEl.querySelector('.datepicker-done'); - this.cancelBtn = this.modalEl.querySelector('.datepicker-cancel'); + this.doneBtn = this.containerEl.querySelector('.datepicker-done'); + this.cancelBtn = this.containerEl.querySelector('.datepicker-cancel'); this.formats = { d: (date: Date) => { @@ -1305,8 +1306,7 @@ export class Datepicker extends Component { static { Datepicker._template = ` -
- -
- `; + `; } } From 1e9c276f54b8e38a70f414b392d17b8df84f4316 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 20:33:33 +0100 Subject: [PATCH 13/48] enhancement(Datepicker) refactor modal css selectors --- sass/components/_datepicker.scss | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sass/components/_datepicker.scss b/sass/components/_datepicker.scss index 16a99d45a9..caf2073eba 100644 --- a/sass/components/_datepicker.scss +++ b/sass/components/_datepicker.scss @@ -1,14 +1,16 @@ /* Modal */ -.datepicker-modal { +// @removed since v2.2.1 +/*.datepicker-modal { max-width: 325px; // @removed since v2.2.1-dev regarding Material M3 standards - /* min-width: 300px; - max-height: none; */ -} + min-width: 300px; + max-height: none; +}*/ -.datepicker-container.modal-content { +.datepicker-container { display: flex; flex-direction: column; + max-width: 325px; padding: 0; background-color: var(--md-sys-color-surface); } @@ -238,7 +240,7 @@ /* Media Queries */ @media #{$medium-and-up} { - @removed since v2.2.1-dev regarding Material M3 standards + // @removed since v2.2.1-dev regarding Material M3 standards /*.datepicker-modal { max-width: 625px; } From b599ff23a3e59dba57a21aac753a44c69784940a Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 20:35:44 +0100 Subject: [PATCH 14/48] enhancement(Datepicker) implemented input field callback functions #558 --- src/datepicker.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/datepicker.ts b/src/datepicker.ts index 46c747801b..676bf9c71e 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -143,7 +143,11 @@ export interface DatepickerOptions extends BaseOptions { * @default null */ onDraw: (() => void) | null; - + /** + * Callback function for interaction with input field. + * @default null + */ + onInputInteraction: (() => void) | null; /** Field used for internal calculations DO NOT CHANGE IT */ minYear?: number; /** Field used for internal calculations DO NOT CHANGE IT */ @@ -245,7 +249,8 @@ const _defaults: DatepickerOptions = { events: [], // callback function onSelect: null, - onDraw: null + onDraw: null, + onInputInteraction: null, }; export class Datepicker extends Component { @@ -1154,6 +1159,7 @@ export class Datepicker extends Component { this.setDateFromInput(e.target as HTMLInputElement); this.draw(); this.gotoDate(e.target === this.el ? this.date : this.endDate); + if (this.options.onInputInteraction) this.options.onInputInteraction.call(this); }; _handleInputKeydown = (e: KeyboardEvent) => { @@ -1161,6 +1167,7 @@ export class Datepicker extends Component { e.preventDefault(); this.setDateFromInput(e.target as HTMLInputElement); this.draw(); + if (this.options.onInputInteraction) this.options.onInputInteraction.call(this); } }; From 4f5c37a33d79657f92b69ef4c30db70292354657 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 20:37:30 +0100 Subject: [PATCH 15/48] enhancement(Datepicker) allow user specified end date element #558 --- src/datepicker.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/datepicker.ts b/src/datepicker.ts index 676bf9c71e..06b13b3282 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -30,6 +30,10 @@ export interface DatepickerOptions extends BaseOptions { * @default false */ isDateRange: boolean; + /** + * The selector of the user specified date range end element + */ + dateRangeEndEl: string | null; /** * The initial condition if the datepicker is based on multiple date selection. * @default false @@ -169,6 +173,8 @@ const _defaults: DatepickerOptions = { parse: null, // The initial condition if the datepicker is based on date range isDateRange: false, + // The selector of the user specified date range end element + dateRangeEndEl: null, // The initial condition if the datepicker is based on multiple date selection isMultipleSelection: false, // The initial date to view when first opened @@ -437,8 +443,14 @@ export class Datepicker extends Component { } if (this.options.isDateRange) { - this.endDateEl = this.createDateInput(); - this.endDateEl.classList.add('datepicker-end-date'); + if (!this.options.dateRangeEndEl) { + this.endDateEl = this.createDateInput(); + this.endDateEl.classList.add('datepicker-end-date'); + } else if(document.querySelector(this.options.dateRangeEndEl) as HTMLInputElement === undefined) { + console.warn('Specified date range end input element in dateRangeEndEl not found'); + } else { + this.endDateEl = document.querySelector(this.options.dateRangeEndEl) as HTMLInputElement; + } } if (this.options.showClearBtn) { @@ -745,8 +757,8 @@ export class Datepicker extends Component { (this.options.maxDate && day > this.options.maxDate) || (this.options.disableWeekends && Datepicker._isWeekend(day)) || (this.options.disableDayFn && this.options.disableDayFn(day)), - isDateRangeStart = this.options.isDateRange && Datepicker._compareDates(this.date, day), - isDateRangeEnd = this.options.isDateRange && Datepicker._compareDates(this.endDate, day), + isDateRangeStart = this.options.isDateRange && this.date && this.endDate && Datepicker._compareDates(this.date, day), + isDateRangeEnd = this.options.isDateRange && this.endDate && Datepicker._compareDates(this.endDate, day), isDateRange = this.options.isDateRange && Datepicker._isDate(this.endDate) && From ae8a06a4be2c6d931af894343f914855b30eab80 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 20:39:12 +0100 Subject: [PATCH 16/48] enhancement(Datepicker) implemented date format rendering in supporting text field if present #558 --- src/datepicker.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/datepicker.ts b/src/datepicker.ts index 06b13b3282..1ebeb70e31 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -442,6 +442,10 @@ export class Datepicker extends Component { this.el.classList.add('datepicker-date-input'); } + if (this.el.parentElement.querySelector('.datepicker-format') !== undefined) { + this._renderDateInputFormat(this.el); + } + if (this.options.isDateRange) { if (!this.options.dateRangeEndEl) { this.endDateEl = this.createDateInput(); @@ -450,6 +454,9 @@ export class Datepicker extends Component { console.warn('Specified date range end input element in dateRangeEndEl not found'); } else { this.endDateEl = document.querySelector(this.options.dateRangeEndEl) as HTMLInputElement; + if (this.endDateEl.parentElement.querySelector('.datepicker-format') !== undefined) { + this._renderDateInputFormat(this.endDateEl); + } } } @@ -472,6 +479,13 @@ export class Datepicker extends Component { } } + /** + * Renders the date input format + */ + _renderDateInputFormat(el: HTMLInputElement) { + el.parentElement.querySelector('.datepicker-format').innerHTML = this.options.format.toString(); + } + /** * Gets a string representation of the given date. */ From 9c455bb7d7c343553bd44573d33260149c45ca9c Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 21:10:05 +0100 Subject: [PATCH 17/48] enhancement(Datepicker) implemented open by default option #558 --- src/datepicker.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/datepicker.ts b/src/datepicker.ts index 1ebeb70e31..ea1a4c8d00 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -110,6 +110,10 @@ export interface DatepickerOptions extends BaseOptions { * @default false */ showDaysInNextAndPreviousMonths: boolean; + /** + * Specify if the docked datepicker is in open state by default + */ + openByDefault: boolean; /** * Specify a DOM element OR selector for a DOM element to render * the calendar in, by default it will be placed before the input. @@ -208,6 +212,8 @@ const _defaults: DatepickerOptions = { showMonthAfterYear: false, // Render days of the calendar grid that fall in the next or previous month showDaysInNextAndPreviousMonths: false, + // Specify if docked picker is in open state by default + openByDefault: false, // Specify a DOM element to render the calendar in container: null, // Show clear button @@ -475,6 +481,7 @@ export class Datepicker extends Component { } else { //this.containerEl.before(this.el); const appendTo = !this.endDateEl ? this.el : this.endDateEl; + if (!this.options.openByDefault) (this.containerEl as HTMLElement).setAttribute('style', 'display: none; visibility: hidden;'); appendTo.parentElement.after(this.containerEl); } } From 3bcbccf9f2fe62c89aa303ece34d411fda70ddb3 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 22:14:02 +0100 Subject: [PATCH 18/48] enhancement(Datepicker) fix inconsistency in datepicker days rendering --- sass/components/_datepicker.scss | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sass/components/_datepicker.scss b/sass/components/_datepicker.scss index caf2073eba..cb3eac879b 100644 --- a/sass/components/_datepicker.scss +++ b/sass/components/_datepicker.scss @@ -46,6 +46,11 @@ vertical-align: middle; } + .select-year input, + .select-month input { + background-color: transparent; + } + .select-year input { width: 50px; } @@ -98,6 +103,12 @@ line-height: 47px; font-weight: 500; } + + .daterange & { + .date-text { + font-size: 1.8rem; + } + } } @@ -257,9 +268,9 @@ .datepicker-table, .datepicker-footer { width: 320px; - }*/ + } .datepicker-day-button { line-height: 44px; - } + }*/ } From 695cbe92402332f13b0e641ccb9ce516f75241d1 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 22:32:42 +0100 Subject: [PATCH 19/48] fix failing spec tests --- spec/tests/datepicker/datepickerSpec.js | 20 ++++++++++---------- src/datepicker.ts | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/spec/tests/datepicker/datepickerSpec.js b/spec/tests/datepicker/datepickerSpec.js index 4f2491dbf6..21ca5459d4 100644 --- a/spec/tests/datepicker/datepickerSpec.js +++ b/spec/tests/datepicker/datepickerSpec.js @@ -16,11 +16,11 @@ describe('Datepicker Plugin', () => { it('can have a string format', (done) => { const input = document.querySelector('#datepickerInput'); const today = new Date(); - M.Datepicker.init(input, { format: 'mm/dd/yyyy' }); + M.Datepicker.init(input, { format: 'mm/dd/yyyy', openByDefault: true }); const datepicker = M.Datepicker.getInstance(input); //datepicker.open(); setTimeout(() => { - const day1 = document.querySelector('.datepicker-modal button[data-day="1"]'); + const day1 = document.querySelector('.datepicker-container button[data-day="1"]'); day1.click(); document.querySelector('.datepicker-done').click(); setTimeout(() => { @@ -37,11 +37,11 @@ describe('Datepicker Plugin', () => { const input = document.querySelector('#datepickerInput'); const today = new Date(); const formatFn = `${today.getFullYear() - 100}-${today.getMonth() + 1}-99`; - M.Datepicker.init(input, { format: formatFn }); + M.Datepicker.init(input, { format: formatFn, openByDefault: true }); const datepicker = M.Datepicker.getInstance(input); //datepicker.open(); setTimeout(() => { - const day1 = document.querySelector('.datepicker-modal button[data-day="1"]'); + const day1 = document.querySelector('.datepicker-container button[data-day="1"]'); day1.click(); document.querySelector('.datepicker-done').click(); setTimeout(() => { @@ -55,7 +55,7 @@ describe('Datepicker Plugin', () => { it('can change the calendar modal selected date by input', (done) => { const input = document.querySelector('#datepickerInput'); - M.Datepicker.init(input, { format: 'mm/dd/yyyy' }); + M.Datepicker.init(input, { format: 'mm/dd/yyyy', openByDefault: true }); const today = new Date(); const month = today.getMonth(); const year = today.getFullYear() - 44; @@ -73,7 +73,7 @@ describe('Datepicker Plugin', () => { const selectedDayElem = document.querySelector(`.datepicker-row td[data-day="${day}"]`); expect( selectMonthElem.querySelector('option[selected="selected"]').value === - (month - 1).toString() + (month - 1).toString() ).toEqual( true, `selected month should be ${month}, given value ${selectMonthElem.querySelector('option[selected="selected"]').value}` @@ -94,7 +94,7 @@ describe('Datepicker Plugin', () => { it('should have a date range input field if date range option is enabled', (done) => { const input = document.querySelector('#datepickerInput'); - M.Datepicker.init(input, { isDateRange: true }); + M.Datepicker.init(input, { isDateRange: true, openByDefault: true }); setTimeout(() => { expect(document.querySelector('.datepicker-end-date')).toExist( 'end date input should exist' @@ -105,13 +105,13 @@ describe('Datepicker Plugin', () => { it('should have multiple input fields if multiple select option is enabled and multiple dates are selected', (done) => { const input = document.querySelector('#datepickerInput'); - M.Datepicker.init(input, { isMultipleSelection: true }); + M.Datepicker.init(input, { isMultipleSelection: true, openByDefault: true }); const datepicker = M.Datepicker.getInstance(input); datepicker.open(); setTimeout(() => { - for (let i = 1; i < 4; i++) { + for (let i = 1; i <= 3; i++) { setTimeout(() => { - document.querySelector(`.datepicker-modal button[data-day="${i}"]`).click(); + document.querySelector(`.datepicker-container button[data-day="${i}"]`).click(); }, i * 10); } setTimeout(() => { diff --git a/src/datepicker.ts b/src/datepicker.ts index ea1a4c8d00..bef32e92a3 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -448,7 +448,7 @@ export class Datepicker extends Component { this.el.classList.add('datepicker-date-input'); } - if (this.el.parentElement.querySelector('.datepicker-format') !== undefined) { + if (!this.el.parentElement.querySelector('.datepicker-format') === null) { this._renderDateInputFormat(this.el); } @@ -460,7 +460,7 @@ export class Datepicker extends Component { console.warn('Specified date range end input element in dateRangeEndEl not found'); } else { this.endDateEl = document.querySelector(this.options.dateRangeEndEl) as HTMLInputElement; - if (this.endDateEl.parentElement.querySelector('.datepicker-format') !== undefined) { + if (!this.endDateEl.parentElement.querySelector('.datepicker-format') === null) { this._renderDateInputFormat(this.endDateEl); } } From bd835535d03b316b33c6800152bf6dae9e07ed76 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Fri, 27 Dec 2024 22:35:34 +0100 Subject: [PATCH 20/48] enhancement(Datepicker) add daterange class programmatically for date display font size adjustment when rendering multiple dates --- src/datepicker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/datepicker.ts b/src/datepicker.ts index bef32e92a3..4f971f1f86 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -453,6 +453,7 @@ export class Datepicker extends Component { } if (this.options.isDateRange) { + this.containerEl.classList.add('daterange'); if (!this.options.dateRangeEndEl) { this.endDateEl = this.createDateInput(); this.endDateEl.classList.add('datepicker-end-date'); From 29ff96931ef668395dcd2cf007e72b43067fcc4f Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Sat, 28 Dec 2024 13:26:23 +0100 Subject: [PATCH 21/48] enhancement(Datepicker) fixed classmapping calendar day element css selectors #558 --- src/datepicker.ts | 66 ++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/src/datepicker.ts b/src/datepicker.ts index 4f971f1f86..a84e567f5e 100644 --- a/src/datepicker.ts +++ b/src/datepicker.ts @@ -643,7 +643,6 @@ export class Datepicker extends Component { * Sets given date as the input value on the given element. */ setInputValue(el, date) { - console.log('setinputvalue'); if (el.type == 'date') { this.setDataDate(el, date); el.value = this.formatDate(date, 'yyyy-mm-dd'); @@ -848,8 +847,21 @@ export class Datepicker extends Component { } renderDay(opts) { - const arr = ['datepicker-day']; - let ariaSelected = 'false'; + const classMap = { + isDisabled: 'is-disabled', + isToday: 'is-today', + isSelected: 'is-selected', + hasEvent: 'has-event', + isInRange: 'is-inrange', + isStartRange: 'is-startrange', + isEndRange: 'is-endrange', + isDateRangeStart: 'is-daterange-start', + isDateRangeEnd: 'is-daterange-end', + isDateRange: 'is-daterange' + }, + ariaSelected = !(['isSelected', 'isDateRange'].filter((prop) => !!(opts.hasOwnProperty(prop) && opts[prop] === true)).length === 0), + arr = ['datepicker-day']; + if (opts.isEmpty) { if (opts.showDaysInNextAndPreviousMonths) { arr.push('is-outside-current-month'); @@ -859,52 +871,12 @@ export class Datepicker extends Component { } } - // @todo wouldn't it be better defining opts class mapping and looping trough opts? - if (opts.isDisabled) { - arr.push('is-disabled'); - } - - if (opts.isToday) { - arr.push('is-today'); - } - - if (opts.isSelected) { - arr.push('is-selected'); - ariaSelected = 'true'; - } - - // @todo should we create this additional css class? - if (opts.hasEvent) { - arr.push('has-event'); - } - - // @todo create additional css class - if (opts.isInRange) { - arr.push('is-inrange'); - ariaSelected = 'true'; - } - - // @todo should we create this additional css class? - if (opts.isStartRange) { - arr.push('is-startrange'); - } - - // @todo should we create this additional css class? - if (opts.isEndRange) { - arr.push('is-endrange'); - } - - if (opts.isDateRangeStart) { - arr.push('is-daterange-start'); - } - - if (opts.isDateRangeEnd) { - arr.push('is-daterange-end'); + for (const [property, className] of Object.entries(classMap)) { + if (opts.hasOwnProperty(property) && opts[property]) { + arr.push(className); + } } - if (opts.isDateRange) { - arr.push('is-daterange'); - } return ( `` + `` + From 2c4e2aece9b1490fd1761d1406a4003979859ed2 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Sat, 28 Dec 2024 13:27:55 +0100 Subject: [PATCH 22/48] enhancement(Datepicker) add implemented day selector classes without styling as todos in scss file --- sass/components/_datepicker.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sass/components/_datepicker.scss b/sass/components/_datepicker.scss index cb3eac879b..c8cadfca8e 100644 --- a/sass/components/_datepicker.scss +++ b/sass/components/_datepicker.scss @@ -157,6 +157,15 @@ } } +// @todo +.datepicker-day.has-event {} +// @todo +.datepicker-day.is-inrange {} +// @todo +.datepicker-day.is-startrange {} +// @todo +.datepicker-day.is-endrange {} + .datepicker-day.is-daterange-start, .datepicker-day.is-daterange-end, .datepicker-day.is-daterange { From 268ae1e540b4645bff4f29037c9353f597e9839c Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Sun, 29 Dec 2024 22:54:32 +0100 Subject: [PATCH 23/48] fix(Timepicker) compatibility with no modal #525 #476 --- src/timepicker.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/timepicker.ts b/src/timepicker.ts index c55d6066bd..e06a74321a 100644 --- a/src/timepicker.ts +++ b/src/timepicker.ts @@ -236,13 +236,13 @@ export class Timepicker extends Component { } _handleInputClick = () => { - this.open(); + this.inputHours.focus(); }; _handleInputKeydown = (e: KeyboardEvent) => { if (Utils.keys.ENTER.includes(e.key)) { e.preventDefault(); - this.open(); + this.inputHours.focus(); } }; @@ -296,9 +296,10 @@ export class Timepicker extends Component { } if (this.currentView === 'hours') { + this.inputMinutes.focus(); this.showView('minutes', this.options.duration / 2); } else { - this.minutesView.classList.add('timepicker-dial-out'); + // this.minutesView.classList.add('timepicker-dial-out'); setTimeout(() => { this.done(); }, this.options.duration / 2); @@ -381,6 +382,8 @@ export class Timepicker extends Component { doneButton.classList.add('timepicker-close'); //doneButton.addEventListener('click', this._finishSelection); confirmationBtnsContainer.appendChild(doneButton); + this._updateTimeFromInput(); + this.showView('hours'); } _clockSetup() { @@ -726,8 +729,7 @@ export class Timepicker extends Component { this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString(); } - // todo: remove e - done = (e = null, clearValue = null) => { + done = (clearValue = null) => { // Set input value const last = this.el.value; let value = clearValue @@ -744,18 +746,14 @@ export class Timepicker extends Component { new Event('change', { bubbles: true, cancelable: true, composed: true }) ); } - //this.el.focus(); - return e; // just for passing linter, can be removed }; clear = () => { - this.done(null, true); + this.done(true); }; // deprecated open() { - // this._updateTimeFromInput(); - // this.showView('hours'); console.warn( 'Timepicker.close() is deprecated. Remove this method and wrap in modal yourself.' ); @@ -777,7 +775,7 @@ export class Timepicker extends Component {
- +
: From 076c4dfb97f4e6303f7318361d8a458bab395b81 Mon Sep 17 00:00:00 2001 From: Geert Selderslaghs Date: Mon, 30 Dec 2024 00:30:43 +0100 Subject: [PATCH 24/48] refactor(Timepicker) remove modal related selectors #525 --- sass/components/_timepicker.scss | 28 +++++++++++++++------------- src/timepicker.ts | 7 ++----- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/sass/components/_timepicker.scss b/sass/components/_timepicker.scss index adb2b4cc0c..b12582694c 100644 --- a/sass/components/_timepicker.scss +++ b/sass/components/_timepicker.scss @@ -1,12 +1,14 @@ /* Timepicker Containers */ -.timepicker-modal { +/* container removed as of v2.2.1 */ +/* .timepicker-container { max-width: 325px; max-height: none; -} +}*/ -.timepicker-container.modal-content { +.timepicker-container { display: flex; flex-direction: column; + max-width: 325px; padding: 0; } @@ -88,8 +90,8 @@ input[type=text].text-primary { max-width: 3.5rem; } -.timepicker-modal .am-btn, -.timepicker-modal .pm-btn { +.timepicker-container .am-btn, +.timepicker-container .pm-btn { width: 3.6rem; height: 50%; padding-left: calc(var(--btn-padding) / 1.6); @@ -101,12 +103,12 @@ input[type=text].text-primary { border: 1px solid var(--md-sys-color-outline); } -.timepicker-modal .am-btn { +.timepicker-container .am-btn { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } -.timepicker-modal .pm-btn { +.timepicker-container .pm-btn { border-top: 0; border-top-left-radius: 0; border-top-right-radius: 0; @@ -224,12 +226,12 @@ input[type=text].text-primary { /* Media Queries */ @media #{$large-and-up} { - .timepicker-modal { + .timepicker-container { width: auto; max-width: 620px; } - .timepicker-container.modal-content { + .timepicker-container { flex-direction: row; } @@ -254,8 +256,8 @@ input[type=text].text-primary { max-width: unset; } - .timepicker-modal .am-btn, - .timepicker-modal .pm-btn { + .timepicker-container .am-btn, + .timepicker-container .pm-btn { width: auto; padding-left: var(--btn-padding); padding-right: var(--btn-padding); @@ -266,12 +268,12 @@ input[type=text].text-primary { text-align: inherit; } - .timepicker-modal .am-btn { + .timepicker-container .am-btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } - .timepicker-modal .pm-btn { + .timepicker-container .pm-btn { border-left: 0; border-bottom-left-radius: 0; border-top-left-radius: 0; diff --git a/src/timepicker.ts b/src/timepicker.ts index e06a74321a..2449a7eb17 100644 --- a/src/timepicker.ts +++ b/src/timepicker.ts @@ -768,9 +768,7 @@ export class Timepicker extends Component { } static { - Timepicker._template = ` -