@expressivecss/expressive 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -1
- package/README.md +1 -1
- package/dist/js/expressive.cjs +55 -8
- package/dist/js/expressive.cjs.map +2 -2
- package/dist/js/expressive.js +55 -8
- package/dist/js/expressive.js.map +2 -2
- package/dist/js/expressive.min.js +9 -9
- package/dist/js/expressive.min.js.map +3 -3
- package/dist/js/expressive.mjs +55 -8
- package/dist/js/expressive.mjs.map +2 -2
- package/dist/types/components/datepicker.d.ts +4 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ the format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.9.1] - 2026-09-08
|
|
9
|
+
|
|
10
|
+
Includes the fixes from [#140](https://github.com/BaezFJ/ExpressiveCSS/pull/140).
|
|
11
|
+
|
|
12
|
+
### Migration from 0.9.0
|
|
13
|
+
|
|
14
|
+
Custom Datepicker row renderers must return calendar rows, cells, and day buttons
|
|
15
|
+
with supported attributes. Use `renderDay` to generate cells and set
|
|
16
|
+
`type="button"` on custom day buttons. Unsupported HTML now throws `TypeError`.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Validate enhanced-select icon URLs and encode calendar identifiers and day values.
|
|
21
|
+
- Restrict Datepicker's low-level row renderers to calendar rows, cells, and day buttons. Custom renderers must use supported attributes and `type="button"`; unsupported HTML now throws `TypeError`.
|
|
22
|
+
- Preserve special characters in generated semantics tables and correct the documentation-script and floating-sheet test patterns.
|
|
23
|
+
|
|
8
24
|
## [0.9.0] - 2026-09-08
|
|
9
25
|
|
|
10
26
|
### Migration from 0.8.0
|
|
@@ -1493,7 +1509,8 @@ are no compatibility aliases.
|
|
|
1493
1509
|
- `.tabs` nested in a header or app bar as a secondary row. `.tabs` is a
|
|
1494
1510
|
standalone component; the `.tabs.transparent` app bar variant is gone with it.
|
|
1495
1511
|
|
|
1496
|
-
[Unreleased]: https://github.com/BaezFJ/ExpressiveCSS/compare/v0.9.
|
|
1512
|
+
[Unreleased]: https://github.com/BaezFJ/ExpressiveCSS/compare/v0.9.1...HEAD
|
|
1513
|
+
[0.9.1]: https://github.com/BaezFJ/ExpressiveCSS/compare/v0.9.0...v0.9.1
|
|
1497
1514
|
[0.9.0]: https://github.com/BaezFJ/ExpressiveCSS/compare/v0.8.0...v0.9.0
|
|
1498
1515
|
[0.8.0]: https://github.com/BaezFJ/ExpressiveCSS/compare/v0.7.0...v0.8.0
|
|
1499
1516
|
[0.7.0]: https://github.com/BaezFJ/ExpressiveCSS/compare/v0.6.0...v0.7.0
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ ExpressiveCSS is a Material Design 3 front-end framework built with Sass and
|
|
|
4
4
|
TypeScript. It provides theme tokens, layout and utility classes, styled form
|
|
5
5
|
controls, and JavaScript components in browser, ES module, and CommonJS builds.
|
|
6
6
|
|
|
7
|
-
> ExpressiveCSS is currently at version `0.9.
|
|
7
|
+
> ExpressiveCSS is currently at version `0.9.1` and under active development.
|
|
8
8
|
|
|
9
9
|
Documentation: [www.expressivecss.com](https://www.expressivecss.com)
|
|
10
10
|
|
package/dist/js/expressive.cjs
CHANGED
|
@@ -4084,11 +4084,21 @@ var FormSelect = class _FormSelect extends Component {
|
|
|
4084
4084
|
}
|
|
4085
4085
|
li.appendChild(span);
|
|
4086
4086
|
const iconUrl = realOption.getAttribute("data-icon");
|
|
4087
|
-
|
|
4087
|
+
let imageUrl;
|
|
4088
4088
|
if (iconUrl) {
|
|
4089
|
+
try {
|
|
4090
|
+
const url = new URL(iconUrl, this.el.ownerDocument.baseURI);
|
|
4091
|
+
if (["http:", "https:", "blob:"].includes(url.protocol) || /^data:image\/(?:avif|bmp|gif|jpeg|png|svg\+xml|webp|x-icon)(?:;|,)/i.test(url.href)) {
|
|
4092
|
+
imageUrl = url;
|
|
4093
|
+
}
|
|
4094
|
+
} catch {
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
const classes = realOption.getAttribute("class")?.split(" ").filter(Boolean);
|
|
4098
|
+
if (imageUrl) {
|
|
4089
4099
|
const img = document.createElement("img");
|
|
4090
4100
|
if (classes?.length) img.classList.add(...classes);
|
|
4091
|
-
img.src =
|
|
4101
|
+
img.src = imageUrl.href;
|
|
4092
4102
|
img.ariaHidden = "true";
|
|
4093
4103
|
li.prepend(img);
|
|
4094
4104
|
}
|
|
@@ -4919,13 +4929,18 @@ var _Datepicker = class _Datepicker extends Component {
|
|
|
4919
4929
|
arr.push(className);
|
|
4920
4930
|
}
|
|
4921
4931
|
}
|
|
4922
|
-
|
|
4932
|
+
const day = _Datepicker._escape(opts.day);
|
|
4933
|
+
const month = _Datepicker._escape(opts.month);
|
|
4934
|
+
const year = _Datepicker._escape(opts.year);
|
|
4935
|
+
return `<td data-day="${day}" class="${arr.join(" ")}" aria-selected="${ariaSelected}"><button class="datepicker-day-button" type="button" data-year="${year}" data-month="${month}" data-day="${day}">${day}</button></td>`;
|
|
4923
4936
|
}
|
|
4937
|
+
/** Assemble calendar cells; reject unsupported elements and attributes. */
|
|
4924
4938
|
renderRow(days, isRTL, isRowSelected) {
|
|
4925
|
-
return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + (isRTL ? days.reverse() : days).join("") + "</tr>";
|
|
4939
|
+
return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + _Datepicker._calendarMarkup((isRTL ? days.reverse() : days).join(""), "tr") + "</tr>";
|
|
4926
4940
|
}
|
|
4941
|
+
/** Assemble calendar rows; reject unsupported elements and attributes. */
|
|
4927
4942
|
renderTable(opts, data, randId) {
|
|
4928
|
-
return '<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' + randId + '">' + this.renderHead(opts) + this.renderBody(data) + "</table></div>";
|
|
4943
|
+
return '<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' + _Datepicker._escape(randId) + '">' + this.renderHead(opts) + this.renderBody(data) + "</table></div>";
|
|
4929
4944
|
}
|
|
4930
4945
|
renderHead(opts) {
|
|
4931
4946
|
const arr = [];
|
|
@@ -4937,8 +4952,40 @@ var _Datepicker = class _Datepicker extends Component {
|
|
|
4937
4952
|
}
|
|
4938
4953
|
return "<thead><tr>" + (opts.isRTL ? arr.reverse() : arr).join("") + "</tr></thead>";
|
|
4939
4954
|
}
|
|
4955
|
+
/** Wrap calendar rows after validating and encoding their contents. */
|
|
4940
4956
|
renderBody(rows) {
|
|
4941
|
-
return "<tbody>" + rows.join("") + "</tbody>";
|
|
4957
|
+
return "<tbody>" + _Datepicker._calendarMarkup(rows.join(""), "tbody") + "</tbody>";
|
|
4958
|
+
}
|
|
4959
|
+
static _calendarMarkup(html, parent) {
|
|
4960
|
+
const template = document.createElement("template");
|
|
4961
|
+
template.innerHTML = html;
|
|
4962
|
+
const attributes = {
|
|
4963
|
+
tr: ["class"],
|
|
4964
|
+
td: ["class", "data-day", "aria-selected"],
|
|
4965
|
+
button: ["class", "type", "data-year", "data-month", "data-day", "aria-label", "disabled", "tabindex"]
|
|
4966
|
+
};
|
|
4967
|
+
const render = (node, container) => {
|
|
4968
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
4969
|
+
if (container === "tbody" || container === "tr") {
|
|
4970
|
+
if (node.textContent.trim()) throw new TypeError("Unsupported calendar row content");
|
|
4971
|
+
}
|
|
4972
|
+
return _Datepicker._escape(node.textContent);
|
|
4973
|
+
}
|
|
4974
|
+
if (!(node instanceof HTMLElement)) throw new TypeError("Unsupported calendar row content");
|
|
4975
|
+
const tag = node.localName === "tr" ? "tr" : node.localName === "td" ? "td" : node.localName === "button" ? "button" : null;
|
|
4976
|
+
if (!tag || !(container === "tbody" && tag === "tr" || container === "tr" && tag === "td" || container === "td" && tag === "button")) {
|
|
4977
|
+
throw new TypeError("Unsupported calendar row element");
|
|
4978
|
+
}
|
|
4979
|
+
if ([...node.attributes].some((attr) => !attributes[tag].includes(attr.name))) {
|
|
4980
|
+
throw new TypeError("Unsupported calendar row attribute");
|
|
4981
|
+
}
|
|
4982
|
+
if (tag === "button" && node.getAttribute("type") !== "button") {
|
|
4983
|
+
throw new TypeError('Calendar day buttons require type="button"');
|
|
4984
|
+
}
|
|
4985
|
+
const attrs = attributes[tag].filter((name) => node.hasAttribute(name)).map((name) => ` ${name}="${_Datepicker._escape(node.getAttribute(name))}"`).join("");
|
|
4986
|
+
return `<${tag}${attrs}>${[...node.childNodes].map((child) => render(child, tag)).join("")}</${tag}>`;
|
|
4987
|
+
};
|
|
4988
|
+
return [...template.content.childNodes].map((node) => render(node, parent)).join("");
|
|
4942
4989
|
}
|
|
4943
4990
|
/**
|
|
4944
4991
|
* Whether each month arrow is usable at this position in the allowed range.
|
|
@@ -4978,7 +5025,7 @@ var _Datepicker = class _Datepicker extends Component {
|
|
|
4978
5025
|
}
|
|
4979
5026
|
renderTitle(instance, c, year, month, refYear, randId) {
|
|
4980
5027
|
const opts = this.options, isMinYear = year === opts.minYear, isMaxYear = year === opts.maxYear;
|
|
4981
|
-
let i, j, arr = [], html = '<div id="' + randId + '" class="datepicker-controls" role="heading" aria-live="assertive">';
|
|
5028
|
+
let i, j, arr = [], html = '<div id="' + _Datepicker._escape(randId) + '" class="datepicker-controls" role="heading" aria-live="assertive">';
|
|
4982
5029
|
const { prev, next } = this._arrowState(year, month);
|
|
4983
5030
|
for (i = 0; i < 12; i++) {
|
|
4984
5031
|
arr.push(
|
|
@@ -7493,7 +7540,7 @@ function AutoInit(context = document.body, options) {
|
|
|
7493
7540
|
}
|
|
7494
7541
|
|
|
7495
7542
|
// src/ts/index.ts
|
|
7496
|
-
var version = "0.9.
|
|
7543
|
+
var version = "0.9.1";
|
|
7497
7544
|
Forms.Init();
|
|
7498
7545
|
Chips.Init();
|
|
7499
7546
|
Slider.Init();
|