@birdapi/velinstyle 1.0.0 → 1.1.0
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/README.de.md +12 -8
- package/README.md +12 -8
- package/cli/cli-manifest.json +1 -1
- package/cli/docgen/extract-a11y.js +4 -2
- package/cli/index.js +4 -2
- package/components/index.js +2 -0
- package/components/runtime/component-loaders.js +2 -0
- package/components/velin-data-table.js +361 -0
- package/components/velin-form-summary.js +348 -0
- package/core/a11y/component-contracts.json +18 -1
- package/core/highlight/languages/go.js +28 -0
- package/core/highlight/languages/python.js +29 -0
- package/core/highlight/languages/rust.js +32 -0
- package/core/highlight/languages/yaml.js +23 -0
- package/core/highlight/registry.js +13 -2
- package/core/meta/build.js +1 -1
- package/dist/chunks/attributes-353XDOAX.js +391 -0
- package/dist/chunks/chunk-ERF5YVP4.js +253 -0
- package/dist/chunks/chunk-QVHYL3R4.js +111 -0
- package/dist/chunks/go-HZ6XTFCK.js +31 -0
- package/dist/chunks/highlight-IBDX4XWS.js +37 -0
- package/dist/chunks/python-BMPKDKNO.js +32 -0
- package/dist/chunks/runtime-entry.js +1 -1
- package/dist/chunks/rust-2BWKI2MN.js +35 -0
- package/dist/chunks/velin-code-block-ITCLIC6T.js +132 -0
- package/dist/chunks/velin-data-table-KK7IDGTP.js +302 -0
- package/dist/chunks/velin-form-summary-XRQ7COQH.js +273 -0
- package/dist/chunks/velin-lightbox-NXA3U2FM.js +149 -0
- package/dist/chunks/yaml-O67LEQYT.js +26 -0
- package/dist/llms.txt +2 -2
- package/dist/search-index.json +28 -2
- package/dist/velin-agent.json +31 -5
- package/dist/velinstyle-components.iife.js +742 -4
- package/dist/velinstyle-components.js +744 -4
- package/dist/velinstyle-components.min.js +85 -85
- package/dist/velinstyle.css +129 -0
- package/dist/velinstyle.d.ts +2 -0
- package/dist/velinstyle.min.css +1 -1
- package/package.json +8 -2
- package/src/base/wc-placeholder.css +7 -0
- package/src/components/data-table.css +93 -0
- package/src/components/form-validation.css +40 -0
- package/src/velinstyle.css +1 -0
|
@@ -5705,6 +5705,146 @@ var init_blade = __esm({
|
|
|
5705
5705
|
}
|
|
5706
5706
|
});
|
|
5707
5707
|
|
|
5708
|
+
// core/highlight/languages/python.js
|
|
5709
|
+
var python_exports = {};
|
|
5710
|
+
__export(python_exports, {
|
|
5711
|
+
default: () => lexPython
|
|
5712
|
+
});
|
|
5713
|
+
function lexPython(code) {
|
|
5714
|
+
return tokenize(code, RULES9);
|
|
5715
|
+
}
|
|
5716
|
+
var RULES9;
|
|
5717
|
+
var init_python = __esm({
|
|
5718
|
+
"core/highlight/languages/python.js"() {
|
|
5719
|
+
init_utils();
|
|
5720
|
+
RULES9 = [
|
|
5721
|
+
{ type: "comment", re: /#[^\n]*/y },
|
|
5722
|
+
// Triple-quoted strings first so the single-quote rules cannot split them.
|
|
5723
|
+
{ type: "string", re: /[rbfu]{0,2}"""[\s\S]*?"""/y },
|
|
5724
|
+
{ type: "string", re: /[rbfu]{0,2}'''[\s\S]*?'''/y },
|
|
5725
|
+
{ type: "string", re: /[rbfu]{0,2}"(?:\\.|[^"\\\n])*"/y },
|
|
5726
|
+
{ type: "string", re: /[rbfu]{0,2}'(?:\\.|[^'\\\n])*'/y },
|
|
5727
|
+
{ type: "number", re: /\b0[xX][\da-fA-F_]+\b|\b\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?j?\b/y },
|
|
5728
|
+
{
|
|
5729
|
+
type: "keyword",
|
|
5730
|
+
re: /\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b/y
|
|
5731
|
+
},
|
|
5732
|
+
{
|
|
5733
|
+
type: "builtin",
|
|
5734
|
+
re: /\b(?:None|True|False|self|cls|abs|all|any|bool|bytes|dict|dir|enumerate|filter|float|format|frozenset|getattr|hasattr|int|isinstance|issubclass|iter|len|list|map|max|min|next|object|open|print|range|repr|reversed|round|set|setattr|sorted|str|sum|super|tuple|type|zip)\b/y
|
|
5735
|
+
},
|
|
5736
|
+
// Multi-character operators first so `->` is not split into `-` and `>`.
|
|
5737
|
+
{ type: "operator", re: /->|:=|\*\*=?|\/\/=?|<<=?|>>=?|[-+*/%&|^~<>!=]=?/y },
|
|
5738
|
+
{ type: "punctuation", re: /[[\]{}(),:;.@]/y },
|
|
5739
|
+
{ type: "identifier", re: /\b[A-Za-z_]\w*\b/y }
|
|
5740
|
+
];
|
|
5741
|
+
}
|
|
5742
|
+
});
|
|
5743
|
+
|
|
5744
|
+
// core/highlight/languages/yaml.js
|
|
5745
|
+
var yaml_exports = {};
|
|
5746
|
+
__export(yaml_exports, {
|
|
5747
|
+
default: () => lexYaml
|
|
5748
|
+
});
|
|
5749
|
+
function lexYaml(code) {
|
|
5750
|
+
return tokenize(code, RULES10);
|
|
5751
|
+
}
|
|
5752
|
+
var RULES10;
|
|
5753
|
+
var init_yaml = __esm({
|
|
5754
|
+
"core/highlight/languages/yaml.js"() {
|
|
5755
|
+
init_utils();
|
|
5756
|
+
RULES10 = [
|
|
5757
|
+
{ type: "comment", re: /#[^\n]*/y },
|
|
5758
|
+
// Document markers and block scalar indicators.
|
|
5759
|
+
{ type: "punctuation", re: /^(?:---|\.\.\.)$/my },
|
|
5760
|
+
{ type: "string", re: /"(?:\\.|[^"\\])*"/y },
|
|
5761
|
+
{ type: "string", re: /'(?:''|[^'])*'/y },
|
|
5762
|
+
// Keys are the primary structure in YAML, so they get the attr-name colour.
|
|
5763
|
+
{ type: "attr-name", re: /^[ \t]*-?[ \t]*[\w.$-]+(?=[ \t]*:(?:[ \t]|$))/my },
|
|
5764
|
+
{ type: "punctuation", re: /^[ \t]*-(?=[ \t]|$)/my },
|
|
5765
|
+
{ type: "keyword", re: /\b(?:true|false|null|yes|no|on|off|~)\b/yi },
|
|
5766
|
+
{ type: "number", re: /\b-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/y },
|
|
5767
|
+
// Anchors, aliases, tags and block scalar headers.
|
|
5768
|
+
{ type: "operator", re: /[&*]\w+|![\w/!-]*|[|>][+-]?\d*(?=\s*$)/my },
|
|
5769
|
+
{ type: "punctuation", re: /[:,[\]{}]/y }
|
|
5770
|
+
];
|
|
5771
|
+
}
|
|
5772
|
+
});
|
|
5773
|
+
|
|
5774
|
+
// core/highlight/languages/go.js
|
|
5775
|
+
var go_exports = {};
|
|
5776
|
+
__export(go_exports, {
|
|
5777
|
+
default: () => lexGo
|
|
5778
|
+
});
|
|
5779
|
+
function lexGo(code) {
|
|
5780
|
+
return tokenize(code, RULES11);
|
|
5781
|
+
}
|
|
5782
|
+
var RULES11;
|
|
5783
|
+
var init_go = __esm({
|
|
5784
|
+
"core/highlight/languages/go.js"() {
|
|
5785
|
+
init_utils();
|
|
5786
|
+
RULES11 = [
|
|
5787
|
+
{ type: "comment", re: /\/\/[^\n]*/y },
|
|
5788
|
+
{ type: "comment", re: /\/\*[\s\S]*?\*\//y },
|
|
5789
|
+
// Raw strings may span lines.
|
|
5790
|
+
{ type: "string", re: /`[^`]*`/y },
|
|
5791
|
+
{ type: "string", re: /"(?:\\.|[^"\\\n])*"/y },
|
|
5792
|
+
{ type: "string", re: /'(?:\\.|[^'\\\n])*'/y },
|
|
5793
|
+
{ type: "number", re: /\b0[xX][\da-fA-F_]+\b|\b\d[\d_]*(?:\.[\d_]*)?(?:[eE][+-]?\d+)?i?\b/y },
|
|
5794
|
+
{
|
|
5795
|
+
type: "keyword",
|
|
5796
|
+
re: /\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/y
|
|
5797
|
+
},
|
|
5798
|
+
{
|
|
5799
|
+
type: "builtin",
|
|
5800
|
+
re: /\b(?:append|bool|byte|cap|clear|close|complex|complex64|complex128|copy|delete|error|float32|float64|imag|int|int8|int16|int32|int64|len|make|max|min|new|nil|panic|print|println|real|recover|rune|string|true|false|iota|uint|uint8|uint16|uint32|uint64|uintptr|any)\b/y
|
|
5801
|
+
},
|
|
5802
|
+
{ type: "operator", re: /:=|\.\.\.|&&|\|\||<-|\+\+|--|<<=?|>>=?|&\^=?|[-+*/%&|^<>!=]=?|~/y },
|
|
5803
|
+
{ type: "punctuation", re: /[[\]{}(),;:.]/y },
|
|
5804
|
+
{ type: "identifier", re: /\b[A-Za-z_]\w*\b/y }
|
|
5805
|
+
];
|
|
5806
|
+
}
|
|
5807
|
+
});
|
|
5808
|
+
|
|
5809
|
+
// core/highlight/languages/rust.js
|
|
5810
|
+
var rust_exports = {};
|
|
5811
|
+
__export(rust_exports, {
|
|
5812
|
+
default: () => lexRust
|
|
5813
|
+
});
|
|
5814
|
+
function lexRust(code) {
|
|
5815
|
+
return tokenize(code, RULES12);
|
|
5816
|
+
}
|
|
5817
|
+
var RULES12;
|
|
5818
|
+
var init_rust = __esm({
|
|
5819
|
+
"core/highlight/languages/rust.js"() {
|
|
5820
|
+
init_utils();
|
|
5821
|
+
RULES12 = [
|
|
5822
|
+
{ type: "comment", re: /\/\/\/?[^\n]*/y },
|
|
5823
|
+
{ type: "comment", re: /\/\*[\s\S]*?\*\//y },
|
|
5824
|
+
// Raw strings close on the same number of hashes they opened with.
|
|
5825
|
+
{ type: "string", re: /b?r(#*)"[\s\S]*?"\1/y },
|
|
5826
|
+
{ type: "string", re: /b?"(?:\\.|[^"\\])*"/y },
|
|
5827
|
+
{ type: "string", re: /b?'(?:\\.|[^'\\])'/y },
|
|
5828
|
+
// Attributes such as #[derive(Debug)] read as annotations.
|
|
5829
|
+
{ type: "builtin", re: /#!?\[[^\]]*\]/y },
|
|
5830
|
+
{ type: "number", re: /\b0[xXbo][\da-fA-F_]+\b|\b\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?(?:[iuf](?:8|16|32|64|128|size))?\b/y },
|
|
5831
|
+
{
|
|
5832
|
+
type: "keyword",
|
|
5833
|
+
re: /\b(?:as|async|await|break|const|continue|crate|dyn|else|enum|extern|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|Self|static|struct|super|trait|type|unsafe|use|where|while)\b/y
|
|
5834
|
+
},
|
|
5835
|
+
{
|
|
5836
|
+
type: "builtin",
|
|
5837
|
+
re: /\b(?:bool|char|f32|f64|i8|i16|i32|i64|i128|isize|str|u8|u16|u32|u64|u128|usize|String|Vec|Option|Result|Some|None|Ok|Err|Box|Rc|Arc|true|false)\b/y
|
|
5838
|
+
},
|
|
5839
|
+
// Lifetimes such as 'a must not be read as an unterminated char literal.
|
|
5840
|
+
{ type: "operator", re: /'[a-z_]\w*\b/y },
|
|
5841
|
+
{ type: "operator", re: /=>|->|::|\.\.=?|&&|\|\||<<=?|>>=?|[-+*/%&|^<>!=]=?|[?@]/y },
|
|
5842
|
+
{ type: "punctuation", re: /[[\]{}(),;:.#]/y },
|
|
5843
|
+
{ type: "identifier", re: /\b[A-Za-z_]\w*!?\b/y }
|
|
5844
|
+
];
|
|
5845
|
+
}
|
|
5846
|
+
});
|
|
5847
|
+
|
|
5708
5848
|
// core/highlight/registry.js
|
|
5709
5849
|
function normalizeLanguage(name) {
|
|
5710
5850
|
const n = (name || "").trim().toLowerCase();
|
|
@@ -5724,11 +5864,14 @@ function normalizeLanguage(name) {
|
|
|
5724
5864
|
md: "markdown",
|
|
5725
5865
|
plaintext: "text",
|
|
5726
5866
|
"plain-text": "text",
|
|
5727
|
-
yml: "
|
|
5728
|
-
yaml: "text",
|
|
5867
|
+
yml: "yaml",
|
|
5729
5868
|
toml: "text",
|
|
5730
5869
|
ini: "text",
|
|
5731
|
-
php8: "php"
|
|
5870
|
+
php8: "php",
|
|
5871
|
+
py: "python",
|
|
5872
|
+
python3: "python",
|
|
5873
|
+
golang: "go",
|
|
5874
|
+
rs: "rust"
|
|
5732
5875
|
};
|
|
5733
5876
|
return map[n] || n;
|
|
5734
5877
|
}
|
|
@@ -5784,7 +5927,15 @@ var init_registry = __esm({
|
|
|
5784
5927
|
markup: () => Promise.resolve().then(() => (init_html(), html_exports)),
|
|
5785
5928
|
console: () => Promise.resolve().then(() => (init_plain(), plain_exports)),
|
|
5786
5929
|
php: () => Promise.resolve().then(() => (init_php(), php_exports)),
|
|
5787
|
-
blade: () => Promise.resolve().then(() => (init_blade(), blade_exports))
|
|
5930
|
+
blade: () => Promise.resolve().then(() => (init_blade(), blade_exports)),
|
|
5931
|
+
python: () => Promise.resolve().then(() => (init_python(), python_exports)),
|
|
5932
|
+
py: () => Promise.resolve().then(() => (init_python(), python_exports)),
|
|
5933
|
+
yaml: () => Promise.resolve().then(() => (init_yaml(), yaml_exports)),
|
|
5934
|
+
yml: () => Promise.resolve().then(() => (init_yaml(), yaml_exports)),
|
|
5935
|
+
go: () => Promise.resolve().then(() => (init_go(), go_exports)),
|
|
5936
|
+
golang: () => Promise.resolve().then(() => (init_go(), go_exports)),
|
|
5937
|
+
rust: () => Promise.resolve().then(() => (init_rust(), rust_exports)),
|
|
5938
|
+
rs: () => Promise.resolve().then(() => (init_rust(), rust_exports))
|
|
5788
5939
|
};
|
|
5789
5940
|
registerLanguage("js", lexJs);
|
|
5790
5941
|
}
|
|
@@ -6307,6 +6458,589 @@ var init_velin_counter = __esm({
|
|
|
6307
6458
|
}
|
|
6308
6459
|
});
|
|
6309
6460
|
|
|
6461
|
+
// components/velin-data-table.js
|
|
6462
|
+
var velin_data_table_exports = {};
|
|
6463
|
+
__export(velin_data_table_exports, {
|
|
6464
|
+
default: () => velin_data_table_default
|
|
6465
|
+
});
|
|
6466
|
+
function sortValue(cell, type) {
|
|
6467
|
+
const raw = cell?.dataset?.sortValue ?? cell?.textContent ?? "";
|
|
6468
|
+
const text2 = raw.trim();
|
|
6469
|
+
if (type === "number") {
|
|
6470
|
+
const num = Number.parseFloat(text2.replace(/[^\d.,-]/g, "").replace(",", "."));
|
|
6471
|
+
return Number.isNaN(num) ? Number.NEGATIVE_INFINITY : num;
|
|
6472
|
+
}
|
|
6473
|
+
if (type === "date") {
|
|
6474
|
+
const time = Date.parse(text2);
|
|
6475
|
+
return Number.isNaN(time) ? Number.NEGATIVE_INFINITY : time;
|
|
6476
|
+
}
|
|
6477
|
+
return text2.toLowerCase();
|
|
6478
|
+
}
|
|
6479
|
+
var SORT_TYPES, warnedMissingName, VelinDataTable, velin_data_table_default;
|
|
6480
|
+
var init_velin_data_table = __esm({
|
|
6481
|
+
"components/velin-data-table.js"() {
|
|
6482
|
+
init_a11y_utils();
|
|
6483
|
+
SORT_TYPES = /* @__PURE__ */ new Set(["text", "number", "date"]);
|
|
6484
|
+
warnedMissingName = false;
|
|
6485
|
+
VelinDataTable = class extends HTMLElement {
|
|
6486
|
+
static get observedAttributes() {
|
|
6487
|
+
return ["page-size", "filter-input", "empty-text", "label"];
|
|
6488
|
+
}
|
|
6489
|
+
constructor() {
|
|
6490
|
+
super();
|
|
6491
|
+
this._table = null;
|
|
6492
|
+
this._page = 1;
|
|
6493
|
+
this._query = "";
|
|
6494
|
+
this._sortIndex = -1;
|
|
6495
|
+
this._sortDirection = "ascending";
|
|
6496
|
+
this._filterEl = null;
|
|
6497
|
+
this._pagination = null;
|
|
6498
|
+
this._emptyRow = null;
|
|
6499
|
+
this._filterTimer = null;
|
|
6500
|
+
this._onFilterInput = this._onFilterInput.bind(this);
|
|
6501
|
+
}
|
|
6502
|
+
connectedCallback() {
|
|
6503
|
+
this.classList.add("velin-data-table");
|
|
6504
|
+
requestAnimationFrame(() => this._init());
|
|
6505
|
+
}
|
|
6506
|
+
disconnectedCallback() {
|
|
6507
|
+
if (this._filterEl) this._filterEl.removeEventListener("input", this._onFilterInput);
|
|
6508
|
+
if (this._filterTimer) clearTimeout(this._filterTimer);
|
|
6509
|
+
}
|
|
6510
|
+
attributeChangedCallback(name, previous, next) {
|
|
6511
|
+
if (previous === next || !this._table) return;
|
|
6512
|
+
if (name === "page-size") {
|
|
6513
|
+
this._page = 1;
|
|
6514
|
+
this._render();
|
|
6515
|
+
} else if (name === "filter-input") {
|
|
6516
|
+
this._bindFilterInput();
|
|
6517
|
+
} else if (name === "label") {
|
|
6518
|
+
this._ensureAccessibleName();
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
6522
|
+
/** @returns {HTMLTableRowElement[]} */
|
|
6523
|
+
get rows() {
|
|
6524
|
+
const body = this._table?.tBodies?.[0];
|
|
6525
|
+
return body ? [...body.rows].filter((row) => row !== this._emptyRow) : [];
|
|
6526
|
+
}
|
|
6527
|
+
/** Rows matching the current filter, across all pages. */
|
|
6528
|
+
get matchingRows() {
|
|
6529
|
+
return this.rows.filter((row) => !row.dataset.velinFiltered);
|
|
6530
|
+
}
|
|
6531
|
+
/** Rows visible on the current page. */
|
|
6532
|
+
get visibleRows() {
|
|
6533
|
+
return this.matchingRows.filter((row) => !row.hidden);
|
|
6534
|
+
}
|
|
6535
|
+
get page() {
|
|
6536
|
+
return this._page;
|
|
6537
|
+
}
|
|
6538
|
+
get pageSize() {
|
|
6539
|
+
const size = Number.parseInt(this.getAttribute("page-size") || "", 10);
|
|
6540
|
+
return Number.isFinite(size) && size > 0 ? size : 0;
|
|
6541
|
+
}
|
|
6542
|
+
get pageCount() {
|
|
6543
|
+
const size = this.pageSize;
|
|
6544
|
+
if (!size) return 1;
|
|
6545
|
+
return Math.max(1, Math.ceil(this.matchingRows.length / size));
|
|
6546
|
+
}
|
|
6547
|
+
/**
|
|
6548
|
+
* @param {number} index Column index
|
|
6549
|
+
* @param {'ascending' | 'descending'} [direction]
|
|
6550
|
+
*/
|
|
6551
|
+
sort(index, direction) {
|
|
6552
|
+
const headers = this._headers();
|
|
6553
|
+
const header = headers[index];
|
|
6554
|
+
if (!header) return;
|
|
6555
|
+
const type = this._sortType(header);
|
|
6556
|
+
if (type === "none") return;
|
|
6557
|
+
this._sortDirection = direction || (this._sortIndex === index && this._sortDirection === "ascending" ? "descending" : "ascending");
|
|
6558
|
+
this._sortIndex = index;
|
|
6559
|
+
const factor = this._sortDirection === "ascending" ? 1 : -1;
|
|
6560
|
+
const body = this._table.tBodies[0];
|
|
6561
|
+
const sorted = this.rows.slice().sort((a, b) => {
|
|
6562
|
+
const av = sortValue(a.cells[index], type);
|
|
6563
|
+
const bv = sortValue(b.cells[index], type);
|
|
6564
|
+
if (av < bv) return -1 * factor;
|
|
6565
|
+
if (av > bv) return 1 * factor;
|
|
6566
|
+
return 0;
|
|
6567
|
+
});
|
|
6568
|
+
for (const row of sorted) body.appendChild(row);
|
|
6569
|
+
this._syncSortState();
|
|
6570
|
+
this._page = 1;
|
|
6571
|
+
this._render();
|
|
6572
|
+
const label = header.dataset.sortLabel || header.textContent.trim();
|
|
6573
|
+
announce(`${label} sorted ${this._sortDirection}`);
|
|
6574
|
+
this.dispatchEvent(new CustomEvent("velin-data-table-sort", {
|
|
6575
|
+
bubbles: true,
|
|
6576
|
+
detail: { index, direction: this._sortDirection, column: label }
|
|
6577
|
+
}));
|
|
6578
|
+
}
|
|
6579
|
+
/** @param {string} query */
|
|
6580
|
+
filter(query) {
|
|
6581
|
+
this._query = String(query || "").trim().toLowerCase();
|
|
6582
|
+
for (const row of this.rows) {
|
|
6583
|
+
const match = !this._query || this._rowText(row).includes(this._query);
|
|
6584
|
+
if (match) delete row.dataset.velinFiltered;
|
|
6585
|
+
else row.dataset.velinFiltered = "true";
|
|
6586
|
+
}
|
|
6587
|
+
this._page = 1;
|
|
6588
|
+
this._render();
|
|
6589
|
+
const count = this.matchingRows.length;
|
|
6590
|
+
announce(count === 1 ? "1 row matches" : `${count} rows match`);
|
|
6591
|
+
this.dispatchEvent(new CustomEvent("velin-data-table-filter", {
|
|
6592
|
+
bubbles: true,
|
|
6593
|
+
detail: { query: this._query, count }
|
|
6594
|
+
}));
|
|
6595
|
+
}
|
|
6596
|
+
/** @param {number} page */
|
|
6597
|
+
goToPage(page) {
|
|
6598
|
+
const target = Math.min(Math.max(1, Math.trunc(page) || 1), this.pageCount);
|
|
6599
|
+
if (target === this._page) return;
|
|
6600
|
+
this._page = target;
|
|
6601
|
+
this._render();
|
|
6602
|
+
announce(`Page ${this._page} of ${this.pageCount}`);
|
|
6603
|
+
this.dispatchEvent(new CustomEvent("velin-data-table-page", {
|
|
6604
|
+
bubbles: true,
|
|
6605
|
+
detail: { page: this._page, pageCount: this.pageCount }
|
|
6606
|
+
}));
|
|
6607
|
+
}
|
|
6608
|
+
// ── Setup ──────────────────────────────────────────────────────────────────
|
|
6609
|
+
_init() {
|
|
6610
|
+
this._table = this.querySelector("table");
|
|
6611
|
+
if (!this._table || !this._table.tBodies.length) return;
|
|
6612
|
+
this._ensureAccessibleName();
|
|
6613
|
+
this._setupSorting();
|
|
6614
|
+
this._bindFilterInput();
|
|
6615
|
+
this._render();
|
|
6616
|
+
}
|
|
6617
|
+
_headers() {
|
|
6618
|
+
const headRow = this._table.tHead?.rows?.[0];
|
|
6619
|
+
return headRow ? [...headRow.cells] : [];
|
|
6620
|
+
}
|
|
6621
|
+
/** @param {HTMLTableCellElement} header */
|
|
6622
|
+
_sortType(header) {
|
|
6623
|
+
const declared = (header.dataset.sort || "").toLowerCase();
|
|
6624
|
+
if (declared === "none") return "none";
|
|
6625
|
+
if (SORT_TYPES.has(declared)) return declared;
|
|
6626
|
+
return this.hasAttribute("sortable") ? "text" : "none";
|
|
6627
|
+
}
|
|
6628
|
+
/**
|
|
6629
|
+
* A table without a name is unusable with a screen reader, so surface it
|
|
6630
|
+
* instead of failing silently (WCAG 1.3.1 / 2.4.6).
|
|
6631
|
+
*/
|
|
6632
|
+
_ensureAccessibleName() {
|
|
6633
|
+
const table = this._table;
|
|
6634
|
+
if (!table) return;
|
|
6635
|
+
const label = this.getAttribute("label");
|
|
6636
|
+
const hasName = table.caption?.textContent.trim() || table.getAttribute("aria-label")?.trim() || table.getAttribute("aria-labelledby")?.trim();
|
|
6637
|
+
if (!hasName && label) {
|
|
6638
|
+
table.setAttribute("aria-label", label);
|
|
6639
|
+
return;
|
|
6640
|
+
}
|
|
6641
|
+
if (!hasName && !warnedMissingName) {
|
|
6642
|
+
warnedMissingName = true;
|
|
6643
|
+
console.warn("[velinstyle] <velin-data-table> needs a <caption>, aria-label, or a label attribute.");
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
/** Sortable headers get a real button so keyboard and AT support come for free. */
|
|
6647
|
+
_setupSorting() {
|
|
6648
|
+
this._headers().forEach((header, index) => {
|
|
6649
|
+
if (this._sortType(header) === "none") return;
|
|
6650
|
+
if (header.querySelector(".velin-data-table__sort")) return;
|
|
6651
|
+
const label = header.textContent.trim();
|
|
6652
|
+
header.dataset.sortLabel = label;
|
|
6653
|
+
header.setAttribute("aria-sort", "none");
|
|
6654
|
+
header.classList.add("velin-data-table__th");
|
|
6655
|
+
const button = document.createElement("button");
|
|
6656
|
+
button.type = "button";
|
|
6657
|
+
button.className = "velin-data-table__sort";
|
|
6658
|
+
button.textContent = label;
|
|
6659
|
+
const icon = document.createElement("span");
|
|
6660
|
+
icon.className = "velin-data-table__sort-icon";
|
|
6661
|
+
icon.setAttribute("aria-hidden", "true");
|
|
6662
|
+
button.appendChild(icon);
|
|
6663
|
+
button.addEventListener("click", () => this.sort(index));
|
|
6664
|
+
header.textContent = "";
|
|
6665
|
+
header.appendChild(button);
|
|
6666
|
+
});
|
|
6667
|
+
}
|
|
6668
|
+
_syncSortState() {
|
|
6669
|
+
this._headers().forEach((header, index) => {
|
|
6670
|
+
if (this._sortType(header) === "none") return;
|
|
6671
|
+
const active = index === this._sortIndex;
|
|
6672
|
+
header.setAttribute("aria-sort", active ? this._sortDirection : "none");
|
|
6673
|
+
header.querySelector(".velin-data-table__sort")?.classList.toggle("velin-data-table__sort--active", active);
|
|
6674
|
+
});
|
|
6675
|
+
}
|
|
6676
|
+
_bindFilterInput() {
|
|
6677
|
+
if (this._filterEl) this._filterEl.removeEventListener("input", this._onFilterInput);
|
|
6678
|
+
const selector = this.getAttribute("filter-input");
|
|
6679
|
+
this._filterEl = selector ? document.querySelector(selector) : null;
|
|
6680
|
+
if (this._filterEl) this._filterEl.addEventListener("input", this._onFilterInput);
|
|
6681
|
+
}
|
|
6682
|
+
_onFilterInput(event) {
|
|
6683
|
+
if (this._filterTimer) clearTimeout(this._filterTimer);
|
|
6684
|
+
const value = event.target.value;
|
|
6685
|
+
this._filterTimer = setTimeout(() => this.filter(value), 150);
|
|
6686
|
+
}
|
|
6687
|
+
/** @param {HTMLTableRowElement} row */
|
|
6688
|
+
_rowText(row) {
|
|
6689
|
+
const scoped = [...row.cells].filter((cell) => cell.hasAttribute("data-filter"));
|
|
6690
|
+
const cells = scoped.length ? scoped : [...row.cells];
|
|
6691
|
+
return cells.map((cell) => cell.textContent || "").join(" ").toLowerCase();
|
|
6692
|
+
}
|
|
6693
|
+
// ── Rendering ──────────────────────────────────────────────────────────────
|
|
6694
|
+
_render() {
|
|
6695
|
+
const size = this.pageSize;
|
|
6696
|
+
this._page = Math.min(this._page, this.pageCount);
|
|
6697
|
+
const matching = this.matchingRows;
|
|
6698
|
+
const start = size ? (this._page - 1) * size : 0;
|
|
6699
|
+
const end = size ? start + size : matching.length;
|
|
6700
|
+
for (const row of this.rows) {
|
|
6701
|
+
const index = matching.indexOf(row);
|
|
6702
|
+
row.hidden = index === -1 || index < start || index >= end;
|
|
6703
|
+
}
|
|
6704
|
+
this._renderEmptyState(matching.length === 0);
|
|
6705
|
+
this._renderPagination();
|
|
6706
|
+
}
|
|
6707
|
+
/** @param {boolean} isEmpty */
|
|
6708
|
+
_renderEmptyState(isEmpty) {
|
|
6709
|
+
if (!isEmpty) {
|
|
6710
|
+
this._emptyRow?.remove();
|
|
6711
|
+
this._emptyRow = null;
|
|
6712
|
+
return;
|
|
6713
|
+
}
|
|
6714
|
+
if (this._emptyRow?.isConnected) return;
|
|
6715
|
+
const columns = this._headers().length || 1;
|
|
6716
|
+
const row = document.createElement("tr");
|
|
6717
|
+
row.className = "velin-data-table__empty";
|
|
6718
|
+
const cell = document.createElement("td");
|
|
6719
|
+
cell.colSpan = columns;
|
|
6720
|
+
cell.textContent = this.getAttribute("empty-text") || "No matching rows";
|
|
6721
|
+
row.appendChild(cell);
|
|
6722
|
+
this._table.tBodies[0].appendChild(row);
|
|
6723
|
+
this._emptyRow = row;
|
|
6724
|
+
}
|
|
6725
|
+
_renderPagination() {
|
|
6726
|
+
if (!this.pageSize || this.pageCount <= 1) {
|
|
6727
|
+
this._pagination?.remove();
|
|
6728
|
+
this._pagination = null;
|
|
6729
|
+
return;
|
|
6730
|
+
}
|
|
6731
|
+
if (!this._pagination?.isConnected) {
|
|
6732
|
+
const nav = document.createElement("nav");
|
|
6733
|
+
nav.className = "velin-data-table__pagination";
|
|
6734
|
+
nav.setAttribute("aria-label", this.getAttribute("pagination-label") || "Table pagination");
|
|
6735
|
+
const previous2 = document.createElement("button");
|
|
6736
|
+
previous2.type = "button";
|
|
6737
|
+
previous2.className = "velin-btn velin-btn--outline velin-btn--sm";
|
|
6738
|
+
previous2.dataset.velinPage = "previous";
|
|
6739
|
+
previous2.textContent = this.getAttribute("previous-text") || "Previous";
|
|
6740
|
+
previous2.addEventListener("click", () => this.goToPage(this._page - 1));
|
|
6741
|
+
const status2 = document.createElement("p");
|
|
6742
|
+
status2.className = "velin-data-table__page-status";
|
|
6743
|
+
status2.dataset.velinPage = "status";
|
|
6744
|
+
const next2 = document.createElement("button");
|
|
6745
|
+
next2.type = "button";
|
|
6746
|
+
next2.className = "velin-btn velin-btn--outline velin-btn--sm";
|
|
6747
|
+
next2.dataset.velinPage = "next";
|
|
6748
|
+
next2.textContent = this.getAttribute("next-text") || "Next";
|
|
6749
|
+
next2.addEventListener("click", () => this.goToPage(this._page + 1));
|
|
6750
|
+
nav.append(previous2, status2, next2);
|
|
6751
|
+
this.appendChild(nav);
|
|
6752
|
+
this._pagination = nav;
|
|
6753
|
+
}
|
|
6754
|
+
const status = this._pagination.querySelector('[data-velin-page="status"]');
|
|
6755
|
+
if (status) status.textContent = `Page ${this._page} of ${this.pageCount}`;
|
|
6756
|
+
const previous = this._pagination.querySelector('[data-velin-page="previous"]');
|
|
6757
|
+
const next = this._pagination.querySelector('[data-velin-page="next"]');
|
|
6758
|
+
if (previous) previous.disabled = this._page <= 1;
|
|
6759
|
+
if (next) next.disabled = this._page >= this.pageCount;
|
|
6760
|
+
}
|
|
6761
|
+
};
|
|
6762
|
+
customElements.define("velin-data-table", VelinDataTable);
|
|
6763
|
+
velin_data_table_default = VelinDataTable;
|
|
6764
|
+
}
|
|
6765
|
+
});
|
|
6766
|
+
|
|
6767
|
+
// components/velin-form-summary.js
|
|
6768
|
+
var velin_form_summary_exports = {};
|
|
6769
|
+
__export(velin_form_summary_exports, {
|
|
6770
|
+
default: () => velin_form_summary_default
|
|
6771
|
+
});
|
|
6772
|
+
function escapeSelector(value) {
|
|
6773
|
+
const text2 = String(value ?? "");
|
|
6774
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") return CSS.escape(text2);
|
|
6775
|
+
return text2.replace(/[^\w-]/g, (char) => `\\${char}`);
|
|
6776
|
+
}
|
|
6777
|
+
function fieldLabel(field) {
|
|
6778
|
+
const explicit = field.getAttribute("data-error-label");
|
|
6779
|
+
if (explicit) return explicit;
|
|
6780
|
+
const ariaLabel = field.getAttribute("aria-label");
|
|
6781
|
+
if (ariaLabel?.trim()) return ariaLabel.trim();
|
|
6782
|
+
const labelledBy = field.getAttribute("aria-labelledby");
|
|
6783
|
+
if (labelledBy) {
|
|
6784
|
+
const text2 = labelledBy.split(/\s+/).map((id) => field.ownerDocument.getElementById(id)?.textContent?.trim() || "").filter(Boolean).join(" ");
|
|
6785
|
+
if (text2) return text2;
|
|
6786
|
+
}
|
|
6787
|
+
if (field.id) {
|
|
6788
|
+
const label = field.ownerDocument.querySelector(`label[for="${escapeSelector(field.id)}"]`);
|
|
6789
|
+
if (label?.textContent.trim()) return label.textContent.trim();
|
|
6790
|
+
}
|
|
6791
|
+
const wrapping = field.closest("label");
|
|
6792
|
+
if (wrapping?.textContent.trim()) return wrapping.textContent.trim();
|
|
6793
|
+
return field.name || "This field";
|
|
6794
|
+
}
|
|
6795
|
+
function fieldMessage(field) {
|
|
6796
|
+
return field.getAttribute("data-error-message")?.trim() || field.validationMessage || "Invalid value";
|
|
6797
|
+
}
|
|
6798
|
+
function addDescribedBy(field, id) {
|
|
6799
|
+
const ids = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
|
|
6800
|
+
if (!ids.includes(id)) {
|
|
6801
|
+
ids.push(id);
|
|
6802
|
+
field.setAttribute("aria-describedby", ids.join(" "));
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6805
|
+
function removeDescribedBy(field, id) {
|
|
6806
|
+
const ids = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(Boolean);
|
|
6807
|
+
const next = ids.filter((value) => value !== id);
|
|
6808
|
+
if (next.length) field.setAttribute("aria-describedby", next.join(" "));
|
|
6809
|
+
else field.removeAttribute("aria-describedby");
|
|
6810
|
+
}
|
|
6811
|
+
var FIELD_SELECTOR, IGNORED_TYPES, fieldIdCounter, VelinFormSummary, velin_form_summary_default;
|
|
6812
|
+
var init_velin_form_summary = __esm({
|
|
6813
|
+
"components/velin-form-summary.js"() {
|
|
6814
|
+
init_a11y_utils();
|
|
6815
|
+
FIELD_SELECTOR = "input, select, textarea";
|
|
6816
|
+
IGNORED_TYPES = /* @__PURE__ */ new Set(["submit", "reset", "button", "image", "hidden"]);
|
|
6817
|
+
fieldIdCounter = 0;
|
|
6818
|
+
VelinFormSummary = class extends HTMLElement {
|
|
6819
|
+
static get observedAttributes() {
|
|
6820
|
+
return ["for", "heading"];
|
|
6821
|
+
}
|
|
6822
|
+
constructor() {
|
|
6823
|
+
super();
|
|
6824
|
+
this._form = null;
|
|
6825
|
+
this._panel = null;
|
|
6826
|
+
this._errors = [];
|
|
6827
|
+
this._onSubmit = this._onSubmit.bind(this);
|
|
6828
|
+
this._onFieldChange = this._onFieldChange.bind(this);
|
|
6829
|
+
this._onReset = this._onReset.bind(this);
|
|
6830
|
+
}
|
|
6831
|
+
connectedCallback() {
|
|
6832
|
+
this.classList.add("velin-form-summary");
|
|
6833
|
+
requestAnimationFrame(() => this._bindForm());
|
|
6834
|
+
}
|
|
6835
|
+
disconnectedCallback() {
|
|
6836
|
+
this._unbindForm();
|
|
6837
|
+
}
|
|
6838
|
+
attributeChangedCallback(name, previous, next) {
|
|
6839
|
+
if (previous === next) return;
|
|
6840
|
+
if (name === "for" && this.isConnected) {
|
|
6841
|
+
this._unbindForm();
|
|
6842
|
+
this._bindForm();
|
|
6843
|
+
} else if (name === "heading" && this._panel) {
|
|
6844
|
+
const heading = this._panel.querySelector(".velin-form-summary__heading");
|
|
6845
|
+
if (heading) heading.textContent = this.headingText;
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
6849
|
+
get form() {
|
|
6850
|
+
return this._form;
|
|
6851
|
+
}
|
|
6852
|
+
/** @returns {{ field: HTMLElement, label: string, message: string }[]} */
|
|
6853
|
+
get errors() {
|
|
6854
|
+
return this._errors.slice();
|
|
6855
|
+
}
|
|
6856
|
+
get headingText() {
|
|
6857
|
+
return this.getAttribute("heading") || "There is a problem";
|
|
6858
|
+
}
|
|
6859
|
+
/** Validate the form and render the summary. @returns {boolean} valid */
|
|
6860
|
+
validate() {
|
|
6861
|
+
if (!this._form) return true;
|
|
6862
|
+
const errors = [];
|
|
6863
|
+
for (const field of this._fields()) {
|
|
6864
|
+
if (field.checkValidity()) {
|
|
6865
|
+
this._clearFieldError(field);
|
|
6866
|
+
continue;
|
|
6867
|
+
}
|
|
6868
|
+
const error = { field, label: fieldLabel(field), message: fieldMessage(field) };
|
|
6869
|
+
this._markFieldError(field, error.message);
|
|
6870
|
+
errors.push(error);
|
|
6871
|
+
}
|
|
6872
|
+
this._errors = errors;
|
|
6873
|
+
this._render();
|
|
6874
|
+
return errors.length === 0;
|
|
6875
|
+
}
|
|
6876
|
+
/** Remove the summary and all field error state. */
|
|
6877
|
+
clear() {
|
|
6878
|
+
for (const field of this._fields()) this._clearFieldError(field);
|
|
6879
|
+
this._errors = [];
|
|
6880
|
+
this._render();
|
|
6881
|
+
}
|
|
6882
|
+
/** Move focus to the first field with an error. */
|
|
6883
|
+
focusFirstError() {
|
|
6884
|
+
const first = this._errors[0];
|
|
6885
|
+
if (first) this._focusField(first.field);
|
|
6886
|
+
}
|
|
6887
|
+
// ── Form wiring ────────────────────────────────────────────────────────────
|
|
6888
|
+
_bindForm() {
|
|
6889
|
+
const id = this.getAttribute("for");
|
|
6890
|
+
this._form = id ? this.ownerDocument.getElementById(id) : this.closest("form");
|
|
6891
|
+
if (!this._form) return;
|
|
6892
|
+
if (!this.hasAttribute("native-validation")) this._form.noValidate = true;
|
|
6893
|
+
this._form.addEventListener("submit", this._onSubmit);
|
|
6894
|
+
this._form.addEventListener("reset", this._onReset);
|
|
6895
|
+
this._form.addEventListener("input", this._onFieldChange);
|
|
6896
|
+
this._form.addEventListener("change", this._onFieldChange);
|
|
6897
|
+
}
|
|
6898
|
+
_unbindForm() {
|
|
6899
|
+
if (!this._form) return;
|
|
6900
|
+
this._form.removeEventListener("submit", this._onSubmit);
|
|
6901
|
+
this._form.removeEventListener("reset", this._onReset);
|
|
6902
|
+
this._form.removeEventListener("input", this._onFieldChange);
|
|
6903
|
+
this._form.removeEventListener("change", this._onFieldChange);
|
|
6904
|
+
this._form = null;
|
|
6905
|
+
}
|
|
6906
|
+
/** @returns {HTMLElement[]} */
|
|
6907
|
+
_fields() {
|
|
6908
|
+
if (!this._form) return [];
|
|
6909
|
+
const seenRadioNames = /* @__PURE__ */ new Set();
|
|
6910
|
+
return [...this._form.querySelectorAll(FIELD_SELECTOR)].filter((field) => {
|
|
6911
|
+
if (IGNORED_TYPES.has(field.type)) return false;
|
|
6912
|
+
if (field.disabled || field.hasAttribute("data-error-ignore")) return false;
|
|
6913
|
+
if (typeof field.checkValidity !== "function") return false;
|
|
6914
|
+
if (field.type === "radio" && field.name) {
|
|
6915
|
+
if (seenRadioNames.has(field.name)) return false;
|
|
6916
|
+
seenRadioNames.add(field.name);
|
|
6917
|
+
}
|
|
6918
|
+
return true;
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
_onSubmit(event) {
|
|
6922
|
+
if (this.validate()) return;
|
|
6923
|
+
event.preventDefault();
|
|
6924
|
+
this._announceErrors();
|
|
6925
|
+
this._focusPanel();
|
|
6926
|
+
this.dispatchEvent(new CustomEvent("velin-form-invalid", {
|
|
6927
|
+
bubbles: true,
|
|
6928
|
+
detail: { errors: this.errors.map(({ label, message }) => ({ label, message })) }
|
|
6929
|
+
}));
|
|
6930
|
+
}
|
|
6931
|
+
_onReset() {
|
|
6932
|
+
requestAnimationFrame(() => this.clear());
|
|
6933
|
+
}
|
|
6934
|
+
/** Re-validate a single field once it already had an error, never before. */
|
|
6935
|
+
_onFieldChange(event) {
|
|
6936
|
+
const field = event.target;
|
|
6937
|
+
if (!field || !this._errors.some((error) => error.field === field)) return;
|
|
6938
|
+
if (!field.checkValidity()) return;
|
|
6939
|
+
this._clearFieldError(field);
|
|
6940
|
+
this._errors = this._errors.filter((error) => error.field !== field);
|
|
6941
|
+
this._render();
|
|
6942
|
+
if (this._errors.length === 0) {
|
|
6943
|
+
this.dispatchEvent(new CustomEvent("velin-form-valid", { bubbles: true }));
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
// ── Field state ────────────────────────────────────────────────────────────
|
|
6947
|
+
/** @param {HTMLElement} field */
|
|
6948
|
+
_errorId(field) {
|
|
6949
|
+
if (!field.id) field.id = `velin-field-${++fieldIdCounter}`;
|
|
6950
|
+
return `${field.id}-error`;
|
|
6951
|
+
}
|
|
6952
|
+
/**
|
|
6953
|
+
* @param {HTMLElement} field
|
|
6954
|
+
* @param {string} message
|
|
6955
|
+
*/
|
|
6956
|
+
_markFieldError(field, message) {
|
|
6957
|
+
const errorId = this._errorId(field);
|
|
6958
|
+
field.setAttribute("aria-invalid", "true");
|
|
6959
|
+
let holder = this.ownerDocument.getElementById(errorId);
|
|
6960
|
+
if (!holder) {
|
|
6961
|
+
holder = this._form.querySelector(`[data-velin-error-for="${escapeSelector(field.name || field.id)}"]`);
|
|
6962
|
+
}
|
|
6963
|
+
if (!holder) {
|
|
6964
|
+
holder = this.ownerDocument.createElement("p");
|
|
6965
|
+
holder.dataset.velinErrorGenerated = "true";
|
|
6966
|
+
field.insertAdjacentElement("afterend", holder);
|
|
6967
|
+
}
|
|
6968
|
+
holder.id = errorId;
|
|
6969
|
+
holder.classList.add("velin-field-error");
|
|
6970
|
+
holder.textContent = message;
|
|
6971
|
+
addDescribedBy(field, errorId);
|
|
6972
|
+
}
|
|
6973
|
+
/** @param {HTMLElement} field */
|
|
6974
|
+
_clearFieldError(field) {
|
|
6975
|
+
if (!field.id) return;
|
|
6976
|
+
const errorId = `${field.id}-error`;
|
|
6977
|
+
field.removeAttribute("aria-invalid");
|
|
6978
|
+
removeDescribedBy(field, errorId);
|
|
6979
|
+
const holder = this.ownerDocument.getElementById(errorId);
|
|
6980
|
+
if (!holder) return;
|
|
6981
|
+
if (holder.dataset.velinErrorGenerated) holder.remove();
|
|
6982
|
+
else holder.textContent = "";
|
|
6983
|
+
}
|
|
6984
|
+
/** @param {HTMLElement} field */
|
|
6985
|
+
_focusField(field) {
|
|
6986
|
+
const target = field.type === "radio" && field.name ? this._form.querySelector(`input[type="radio"][name="${escapeSelector(field.name)}"]`) || field : field;
|
|
6987
|
+
target.focus();
|
|
6988
|
+
this.dispatchEvent(new CustomEvent("velin-form-error-focus", {
|
|
6989
|
+
bubbles: true,
|
|
6990
|
+
detail: { name: target.name || target.id }
|
|
6991
|
+
}));
|
|
6992
|
+
}
|
|
6993
|
+
// ── Summary panel ──────────────────────────────────────────────────────────
|
|
6994
|
+
_render() {
|
|
6995
|
+
if (!this._errors.length) {
|
|
6996
|
+
this._panel?.remove();
|
|
6997
|
+
this._panel = null;
|
|
6998
|
+
this.hidden = true;
|
|
6999
|
+
return;
|
|
7000
|
+
}
|
|
7001
|
+
this.hidden = false;
|
|
7002
|
+
if (!this._panel?.isConnected) {
|
|
7003
|
+
const panel = this.ownerDocument.createElement("div");
|
|
7004
|
+
panel.className = "velin-form-summary__panel velin-alert velin-alert--danger";
|
|
7005
|
+
panel.setAttribute("role", "alert");
|
|
7006
|
+
panel.tabIndex = -1;
|
|
7007
|
+
const heading = this.ownerDocument.createElement("p");
|
|
7008
|
+
heading.className = "velin-form-summary__heading";
|
|
7009
|
+
heading.textContent = this.headingText;
|
|
7010
|
+
const list2 = this.ownerDocument.createElement("ul");
|
|
7011
|
+
list2.className = "velin-form-summary__list";
|
|
7012
|
+
panel.append(heading, list2);
|
|
7013
|
+
this.appendChild(panel);
|
|
7014
|
+
this._panel = panel;
|
|
7015
|
+
}
|
|
7016
|
+
const list = this._panel.querySelector(".velin-form-summary__list");
|
|
7017
|
+
list.textContent = "";
|
|
7018
|
+
for (const error of this._errors) {
|
|
7019
|
+
const item = this.ownerDocument.createElement("li");
|
|
7020
|
+
const link = this.ownerDocument.createElement("a");
|
|
7021
|
+
link.href = `#${this._errorId(error.field).replace(/-error$/, "")}`;
|
|
7022
|
+
link.textContent = `${error.label}: ${error.message}`;
|
|
7023
|
+
link.addEventListener("click", (event) => {
|
|
7024
|
+
event.preventDefault();
|
|
7025
|
+
this._focusField(error.field);
|
|
7026
|
+
});
|
|
7027
|
+
item.appendChild(link);
|
|
7028
|
+
list.appendChild(item);
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
_focusPanel() {
|
|
7032
|
+
this._panel?.focus();
|
|
7033
|
+
}
|
|
7034
|
+
_announceErrors() {
|
|
7035
|
+
const count = this._errors.length;
|
|
7036
|
+
announce(count === 1 ? "1 field needs attention" : `${count} fields need attention`, "assertive");
|
|
7037
|
+
}
|
|
7038
|
+
};
|
|
7039
|
+
customElements.define("velin-form-summary", VelinFormSummary);
|
|
7040
|
+
velin_form_summary_default = VelinFormSummary;
|
|
7041
|
+
}
|
|
7042
|
+
});
|
|
7043
|
+
|
|
6310
7044
|
// components/velin-live-dot.js
|
|
6311
7045
|
var velin_live_dot_exports = {};
|
|
6312
7046
|
__export(velin_live_dot_exports, {
|
|
@@ -7404,10 +8138,12 @@ var init_component_loaders = __esm({
|
|
|
7404
8138
|
"velin-copy": () => Promise.resolve().then(() => (init_velin_copy(), velin_copy_exports)),
|
|
7405
8139
|
"velin-countdown": () => Promise.resolve().then(() => (init_velin_countdown(), velin_countdown_exports)),
|
|
7406
8140
|
"velin-counter": () => Promise.resolve().then(() => (init_velin_counter(), velin_counter_exports)),
|
|
8141
|
+
"velin-data-table": () => Promise.resolve().then(() => (init_velin_data_table(), velin_data_table_exports)),
|
|
7407
8142
|
"velin-dialog": () => Promise.resolve().then(() => (init_velin_dialog(), velin_dialog_exports)),
|
|
7408
8143
|
"velin-drawer": () => Promise.resolve().then(() => (init_velin_drawer(), velin_drawer_exports)),
|
|
7409
8144
|
"velin-dropdown": () => Promise.resolve().then(() => (init_velin_dropdown(), velin_dropdown_exports)),
|
|
7410
8145
|
"velin-email": () => Promise.resolve().then(() => (init_velin_email(), velin_email_exports)),
|
|
8146
|
+
"velin-form-summary": () => Promise.resolve().then(() => (init_velin_form_summary(), velin_form_summary_exports)),
|
|
7411
8147
|
"velin-icon": () => Promise.resolve().then(() => (init_velin_icon(), velin_icon_exports)),
|
|
7412
8148
|
"velin-lightbox": () => Promise.resolve().then(() => (init_velin_lightbox(), velin_lightbox_exports)),
|
|
7413
8149
|
"velin-live-dot": () => Promise.resolve().then(() => (init_velin_live_dot(), velin_live_dot_exports)),
|
|
@@ -8023,6 +8759,8 @@ init_velin_sparkline();
|
|
|
8023
8759
|
init_velin_counter();
|
|
8024
8760
|
init_velin_live_dot();
|
|
8025
8761
|
init_velin_code_block();
|
|
8762
|
+
init_velin_data_table();
|
|
8763
|
+
init_velin_form_summary();
|
|
8026
8764
|
init_velin_search();
|
|
8027
8765
|
init_velin_search();
|
|
8028
8766
|
|
|
@@ -8254,10 +8992,12 @@ export {
|
|
|
8254
8992
|
velin_copy_default as VelinCopy,
|
|
8255
8993
|
velin_countdown_default as VelinCountdown,
|
|
8256
8994
|
velin_counter_default as VelinCounter,
|
|
8995
|
+
velin_data_table_default as VelinDataTable,
|
|
8257
8996
|
velin_dialog_default as VelinDialog,
|
|
8258
8997
|
velin_drawer_default as VelinDrawer,
|
|
8259
8998
|
velin_dropdown_default as VelinDropdown,
|
|
8260
8999
|
velin_email_default as VelinEmail,
|
|
9000
|
+
velin_form_summary_default as VelinFormSummary,
|
|
8261
9001
|
VelinHapticObserver,
|
|
8262
9002
|
velin_icon_default as VelinIcon,
|
|
8263
9003
|
velin_lightbox_default as VelinLightbox,
|