@nysds/nys-table 1.13.0 → 1.13.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/dist/nys-table.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { LitElement as v, unsafeCSS as g, html as _ } from "lit";
2
2
  import { property as h, state as u } from "lit/decorators.js";
3
- const x = ':host{--_nys-table-width: 100%;--_nys-table-radius: var(--nys-radius-xl, 12px);--_nys-table-padding: var(--nys-space-100, 8px);--_nys-table-border-color: transparent;--_nys-table-border-width: 0;--_nys-table-font-family: var( --nys-font-family-ui, var( --nys-font-family-sans, "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif ) );--_nys-table-font-size: var(--nys-font-size-ui-md, 16px);--_nys-table-font-weight: var(--nys-font-weight-regular, 400);--_nys-table-line-height: 16px;--_nys-table-padding--caption: var(--nys-space-250, 20px) var(--nys-space-150, 12px);--_nys-table-font-size--caption: var(--nys-font-size-ui-lg, 18px);--_nys-table-font-weight--caption: var(--nys-font-weight-bold, 700);--_nys-table-padding--cell--x: var(--nys-space-150, 12px);--_nys-table-padding--cell--y: var(--nys-space-200, 16px);--_nys-table-background-color: var(--nys-color-ink-reverse, #fff);--_nys-table-background-color--striped: var(--nys-color-neutral-10, #f6f6f6)}:host([bordered]){--_nys-table-border-color: var(--nys-color-neutral-100, #d0d0ce);--_nys-table-border-width: var(--nys-space-1px, 1px)}:host([download]){display:flex;flex-direction:column;gap:var(--nys-space-150, 12px)}.nys-table{width:var(--_nys-table-width);font:var(--_nys-table-font-weight) var(--_nys-table-font-size)/var(--_nys-table-line-height) var(--_nys-table-font-family)}.nys-table table{width:var(--_nys-table-width);border-collapse:collapse;background-color:var(--_nys-table-background-color)}.nys-table caption{padding:var(--_nys-table-padding--caption);font-size:var(--_nys-table-font-size--caption);font-weight:var(--_nys-table-font-weight--caption);text-align:start}.nys-table caption div{display:flex;justify-content:space-between;align-items:center}.nys-table td{padding:var(--_nys-table-padding--cell--y) var(--_nys-table-padding--cell--x);border:var(--_nys-table-border-width) solid var(--_nys-table-border-color)}.nys-table th{border:var(--_nys-table-border-width) solid var(--_nys-table-border-color);overflow:hidden;text-overflow:ellipsis;padding:var(--_nys-table-padding--cell--y) var(--_nys-table-padding--cell--x)}.nys-table th:has(nys-button){padding:0}.nys-table th p{margin:0}.nys-table th nys-button{margin:0;width:auto;justify-content:space-between;--_nys-button-border-width: 0;--_nys-button-border-radius--start: 0;--_nys-button-border-radius--end: 0;--_nys-button-padding--x: var(--_nys-table-padding--cell--x);--_nys-button-justify-content: space-between;--_nys-button-outline-offset: -2px}.nys-table th.nys-table__sortedcolumn{background-color:var(--nys-color-theme-weak, #cddde9)}.nys-table td.nys-table__sortedcolumn{position:relative;z-index:0}.nys-table td.nys-table__sortedcolumn:after{content:"";position:absolute;inset:0;background-color:var(--nys-color-theme, #154973);opacity:.1;pointer-events:none;z-index:-1}:host([striped]) .nys-table tbody tr:nth-child(odd){background-color:var(--_nys-table-background-color--striped)}:host([sortable]) .nys-table th{cursor:pointer}.sr-only{border:0!important;clip:rect(1px,1px,1px,1px)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;height:1px!important;overflow:hidden!important;margin:-1px!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}';
4
- var w = Object.defineProperty, c = (m, t, r, s) => {
5
- for (var e = void 0, n = m.length - 1, o; n >= 0; n--)
6
- (o = m[n]) && (e = o(t, r, e) || e);
7
- return e && w(t, r, e), e;
3
+ const w = ':host{--_nys-table-width: 100%;--_nys-table-radius: var(--nys-radius-xl, 12px);--_nys-table-padding: var(--nys-space-100, 8px);--_nys-table-border-color: transparent;--_nys-table-border-width: 0;--_nys-table-font-family: var(--nys-font-family-ui, var(--nys-font-family-sans, "Proxima Nova", "Helvetica Neue", Helvetica, Arial, sans-serif));--_nys-table-font-size: var(--nys-font-size-ui-md, 16px);--_nys-table-font-weight: var(--nys-font-weight-regular, 400);--_nys-table-line-height: 16px;--_nys-table-padding--caption: var(--nys-space-250, 20px) var(--nys-space-150, 12px);--_nys-table-font-size--caption: var(--nys-font-size-ui-lg, 18px);--_nys-table-font-weight--caption: var(--nys-font-weight-bold, 700);--_nys-table-padding--cell--x: var(--nys-space-150, 12px);--_nys-table-padding--cell--y: var(--nys-space-200, 16px);--_nys-table-background-color: var(--nys-color-ink-reverse, #fff);--_nys-table-background-color--striped: var(--nys-color-neutral-10, #f6f6f6)}:host([bordered]){--_nys-table-border-color: var(--nys-color-neutral-100, #d0d0ce);--_nys-table-border-width: var(--nys-space-1px, 1px)}:host([download]){display:flex;flex-direction:column;gap:var(--nys-space-150, 12px)}.nys-table{width:var(--_nys-table-width);font:var(--_nys-table-font-weight) var(--_nys-table-font-size)/var(--_nys-table-line-height) var(--_nys-table-font-family)}.nys-table table{width:var(--_nys-table-width);border-collapse:collapse;background-color:var(--_nys-table-background-color)}.nys-table caption{padding:var(--_nys-table-padding--caption);font-size:var(--_nys-table-font-size--caption);font-weight:var(--_nys-table-font-weight--caption);text-align:start}.nys-table caption div{display:flex;justify-content:space-between;align-items:center}.nys-table td{padding:var(--_nys-table-padding--cell--y) var(--_nys-table-padding--cell--x);border:var(--_nys-table-border-width) solid var(--_nys-table-border-color)}.nys-table th{border:var(--_nys-table-border-width) solid var(--_nys-table-border-color);overflow:hidden;text-overflow:ellipsis;padding:var(--_nys-table-padding--cell--y) var(--_nys-table-padding--cell--x)}.nys-table th:has(nys-button){padding:0}.nys-table th p{margin:0}.nys-table th nys-button{margin:0;width:-moz-available;width:-webkit-fill-available;width:fill-available;justify-content:space-between;--_nys-button-border-width: 0;--_nys-button-border-radius--start: 0;--_nys-button-border-radius--end: 0;--_nys-button-padding--x: var(--_nys-table-padding--cell--x);--_nys-button-justify-content: space-between;--_nys-button-outline-offset: -2px}.nys-table th.nys-table__sortedcolumn{background-color:var(--nys-color-theme-weak, #cddde9)}.nys-table td.nys-table__sortedcolumn{position:relative;z-index:0}.nys-table td.nys-table__sortedcolumn:after{content:"";position:absolute;inset:0;background-color:var(--nys-color-theme, #154973);opacity:.1;pointer-events:none;z-index:-1}:host([striped]) .nys-table tbody tr:nth-child(odd){background-color:var(--_nys-table-background-color--striped)}:host([sortable]) .nys-table th{cursor:pointer}.sr-only{border:0!important;clip:rect(1px,1px,1px,1px)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;height:1px!important;overflow:hidden!important;margin:-1px!important;padding:0!important;position:absolute!important;width:1px!important;white-space:nowrap!important}';
4
+ var x = Object.defineProperty, p = (m, t, s, r) => {
5
+ for (var o = void 0, n = m.length - 1, e; n >= 0; n--)
6
+ (e = m[n]) && (o = e(t, s, o) || o);
7
+ return o && x(t, s, o), o;
8
8
  };
9
9
  let C = 0;
10
10
  const f = class f extends v {
@@ -24,100 +24,109 @@ const f = class f extends v {
24
24
  _handleSlotChange() {
25
25
  const t = this.shadowRoot?.querySelector(
26
26
  "slot"
27
- ), r = this.shadowRoot?.querySelector(
28
- ".nys-table"
27
+ ), s = this.shadowRoot?.querySelector(
28
+ ".table-container"
29
29
  );
30
- if (!t || !r) return;
31
- r.innerHTML = "", t.assignedElements({ flatten: !0 }).forEach((e) => {
32
- if (e.tagName === "TABLE") {
33
- const n = e.cloneNode(!0);
34
- this._normalizeTable(n), this.sortable && this._addSortIcons(n), r.appendChild(n);
35
- }
36
- });
30
+ if (!t || !s) return;
31
+ s.innerHTML = "";
32
+ const o = t.assignedElements({ flatten: !0 }).find((e) => e.tagName === "TABLE");
33
+ if (!o) return;
34
+ const n = o.cloneNode(!0);
35
+ this._normalizeTableDOM(n), this.sortable && this._addSortIcons(n), s.appendChild(n);
37
36
  }
38
- _normalizeTable(t) {
39
- const r = t.querySelector("thead"), s = t.querySelector("tbody");
40
- if (r && s) return;
41
- let e = t.querySelector(
37
+ _normalizeTableDOM(t) {
38
+ const s = t.querySelector("thead"), r = t.querySelector("tbody");
39
+ if (s && r) return;
40
+ const o = t.querySelector(
42
41
  "caption"
43
- );
44
- e?.textContent?.trim() ? this._captionText = e.textContent.trim() : this._captionText = "";
45
- const n = Array.from(t.querySelectorAll("tr"));
42
+ ), n = Array.from(t.querySelectorAll("tr"));
46
43
  if (n.length === 0) return;
47
- const o = document.createElement("thead"), l = document.createElement("tbody"), p = n.findIndex((a) => a.querySelector("th"));
48
- if (p === -1)
44
+ const e = document.createElement("thead"), l = document.createElement("tbody"), y = n.findIndex((a) => a.querySelector("th"));
45
+ if (y === -1)
49
46
  n.forEach((a) => l.appendChild(a));
50
47
  else {
51
- const a = n[p];
52
- a.querySelectorAll("th").forEach((y) => {
53
- Array.from(y.childNodes).forEach((d) => {
54
- if (d.nodeType === Node.TEXT_NODE && d.textContent?.trim()) {
48
+ const a = n[y];
49
+ a.querySelectorAll("th").forEach((d) => {
50
+ Array.from(d.childNodes).forEach((c) => {
51
+ if (c.nodeType === Node.TEXT_NODE && c.textContent?.trim()) {
55
52
  const b = document.createElement("p");
56
- b.textContent = d.textContent, y.replaceChild(b, d);
53
+ b.textContent = c.textContent, d.replaceChild(b, c);
57
54
  }
58
55
  });
59
- }), o.appendChild(a), n.forEach((y, d) => {
60
- d !== p && l.appendChild(y);
56
+ }), e.appendChild(a), n.forEach((d, c) => {
57
+ c !== y && l.appendChild(d);
61
58
  });
62
59
  }
63
- if (t.innerHTML = "", this.sortable) {
64
- e || (e = document.createElement("caption"), e.style.padding = "0");
60
+ if (t.innerHTML = "", o && t.appendChild(o), this.sortable) {
65
61
  const a = document.createElement("span");
66
- a.setAttribute("class", "sr-only"), a.textContent = "Column headers with buttons are sortable.", e.appendChild(a);
62
+ if (a.setAttribute("class", "sr-only"), a.textContent = "Column headers with buttons are sortable.", o)
63
+ o.appendChild(a);
64
+ else {
65
+ const d = document.createElement("caption");
66
+ d.appendChild(a), t.appendChild(d);
67
+ }
67
68
  }
68
- e && t.appendChild(e), t.appendChild(o), t.appendChild(l);
69
+ t.appendChild(e), t.appendChild(l);
70
+ }
71
+ willUpdate() {
72
+ const t = Array.from(this.children).find(
73
+ (o) => o.tagName === "TABLE"
74
+ );
75
+ if (!t) return;
76
+ const r = t.querySelector("caption")?.textContent?.trim() ?? "";
77
+ this._captionText !== r && (this._captionText = r);
69
78
  }
70
79
  _addSortIcons(t) {
71
- const r = Array.from(t.querySelectorAll("thead th"));
72
- r.length !== 0 && r.forEach((s, e) => {
73
- if (s.querySelector("nys-button[part='sort-button']")) return;
74
- const n = s.textContent?.trim();
80
+ const s = Array.from(t.querySelectorAll("thead th"));
81
+ s.length !== 0 && s.forEach((r, o) => {
82
+ if (r.querySelector("nys-button[part='sort-button']")) return;
83
+ const n = r.textContent?.trim();
75
84
  if (!n) return;
76
- s.textContent = "";
77
- const o = document.createElement("nys-button");
78
- o.setAttribute("part", "sort-button"), o.setAttribute("variant", "ghost"), o.setAttribute("label", n), o.setAttribute("suffixIcon", "slotted"), o.setAttribute("fullWidth", "true");
85
+ r.textContent = "";
86
+ const e = document.createElement("nys-button");
87
+ e.setAttribute("part", "sort-button"), e.setAttribute("variant", "ghost"), e.setAttribute("label", n), e.setAttribute("suffixIcon", "slotted"), e.setAttribute("fullWidth", "true");
79
88
  const l = document.createElement("nys-icon");
80
- l.setAttribute("slot", "suffix-icon"), l.setAttribute("name", "height"), l.setAttribute("size", "24"), l.setAttribute("color", "var(--nys-color-text-weak, #4a4d4f)"), o.appendChild(l), o.addEventListener("nys-click", (p) => {
81
- p.stopPropagation(), this._onSortClick(e, t);
82
- }), s.appendChild(o);
89
+ l.setAttribute("slot", "suffix-icon"), l.setAttribute("name", "height"), l.setAttribute("size", "24"), l.setAttribute("color", "var(--nys-color-text-weak, #4a4d4f)"), e.appendChild(l), e.addEventListener("nys-click", (y) => {
90
+ y.stopPropagation(), this._onSortClick(o, t);
91
+ }), r.appendChild(e);
83
92
  });
84
93
  }
85
94
  _updateSortIcons(t) {
86
- t.querySelectorAll("thead th").forEach((s, e) => {
87
- const n = s.querySelector("nys-button[part='sort-button']"), o = n?.querySelector(
95
+ t.querySelectorAll("thead th").forEach((r, o) => {
96
+ const n = r.querySelector("nys-button[part='sort-button']"), e = n?.querySelector(
88
97
  "nys-icon[slot='suffix-icon']"
89
98
  );
90
- if (!(!n || !o))
91
- if (e === this._sortColumn)
92
- switch (s.classList.add("nys-table__sortedcolumn"), this._sortDirection) {
99
+ if (!(!n || !e))
100
+ if (o === this._sortColumn)
101
+ switch (r.classList.add("nys-table__sortedcolumn"), this._sortDirection) {
93
102
  case "asc":
94
- o.setAttribute("name", "straight"), o.setAttribute("color", "var(--nys-color-ink, #1b1b1b)"), o.style.transform = "rotate(0deg)", s.setAttribute("aria-sort", "ascending");
103
+ e.setAttribute("name", "straight"), e.setAttribute("color", "var(--nys-color-ink, #1b1b1b)"), e.style.transform = "rotate(0deg)", r.setAttribute("aria-sort", "ascending");
95
104
  break;
96
105
  case "desc":
97
- o.setAttribute("name", "straight"), o.setAttribute("color", "var(--nys-color-ink, #1b1b1b)"), o.style.transform = "rotate(180deg)", s.setAttribute("aria-sort", "descending");
106
+ e.setAttribute("name", "straight"), e.setAttribute("color", "var(--nys-color-ink, #1b1b1b)"), e.style.transform = "rotate(180deg)", r.setAttribute("aria-sort", "descending");
98
107
  break;
99
108
  }
100
109
  else
101
- s.classList.remove("nys-table__sortedcolumn"), o.setAttribute("name", "height"), o.setAttribute("color", "var(--nys-color-text-weak, #4a4d4f)"), o.style.transform = "", s.removeAttribute("aria-sort");
110
+ r.classList.remove("nys-table__sortedcolumn"), e.setAttribute("name", "height"), e.setAttribute("color", "var(--nys-color-text-weak, #4a4d4f)"), e.style.transform = "", r.removeAttribute("aria-sort");
102
111
  });
103
112
  }
104
- _onSortClick(t, r) {
105
- this._sortColumn !== t ? (this._sortColumn = t, this._sortDirection = "asc") : this._sortDirection = this._sortDirection === "asc" ? "desc" : "asc", this._updateSortIcons(r), this._sortTable(r, t, this._sortDirection);
113
+ _onSortClick(t, s) {
114
+ this._sortColumn !== t ? (this._sortColumn = t, this._sortDirection = "asc") : this._sortDirection = this._sortDirection === "asc" ? "desc" : "asc", this._updateSortIcons(s), this._sortTable(s, t, this._sortDirection);
106
115
  }
107
- _sortTable(t, r, s) {
108
- const e = t.querySelector("tbody");
109
- if (!e) return;
110
- const n = Array.from(e.querySelectorAll("tr"));
111
- n.sort((o, l) => {
112
- const p = o.children[r]?.textContent?.trim() ?? "", a = l.children[r]?.textContent?.trim() ?? "", y = Number(p), d = Number(a);
116
+ _sortTable(t, s, r) {
117
+ const o = t.querySelector("tbody");
118
+ if (!o) return;
119
+ const n = Array.from(o.querySelectorAll("tr"));
120
+ n.sort((e, l) => {
121
+ const y = e.children[s]?.textContent?.trim() ?? "", a = l.children[s]?.textContent?.trim() ?? "", d = Number(y), c = Number(a);
113
122
  let b;
114
- return !isNaN(y) && !isNaN(d) ? b = y - d : b = p.localeCompare(a), s === "asc" ? b : -b;
115
- }), n.forEach((o) => e.appendChild(o)), this._updateSortedColumnStyles(t);
123
+ return !isNaN(d) && !isNaN(c) ? b = d - c : b = y.localeCompare(a), r === "asc" ? b : -b;
124
+ }), n.forEach((e) => o.appendChild(e)), this._updateSortedColumnStyles(t);
116
125
  }
117
126
  _updateSortedColumnStyles(t) {
118
- t.querySelectorAll("tbody tr").forEach((s) => {
119
- Array.from(s.children).forEach((e, n) => {
120
- n === this._sortColumn ? e.classList.add("nys-table__sortedcolumn") : e.classList.remove("nys-table__sortedcolumn");
127
+ t.querySelectorAll("tbody tr").forEach((r) => {
128
+ Array.from(r.children).forEach((o, n) => {
129
+ n === this._sortColumn ? o.classList.add("nys-table__sortedcolumn") : o.classList.remove("nys-table__sortedcolumn");
121
130
  });
122
131
  });
123
132
  }
@@ -145,33 +154,33 @@ const f = class f extends v {
145
154
  `;
146
155
  }
147
156
  };
148
- f.styles = g(x);
157
+ f.styles = g(w);
149
158
  let i = f;
150
- c([
159
+ p([
151
160
  h({ type: String, reflect: !0 })
152
161
  ], i.prototype, "id");
153
- c([
162
+ p([
154
163
  h({ type: String, reflect: !0 })
155
164
  ], i.prototype, "name");
156
- c([
165
+ p([
157
166
  h({ type: Boolean, reflect: !0 })
158
167
  ], i.prototype, "striped");
159
- c([
168
+ p([
160
169
  h({ type: Boolean, reflect: !0 })
161
170
  ], i.prototype, "sortable");
162
- c([
171
+ p([
163
172
  h({ type: Boolean, reflect: !0 })
164
173
  ], i.prototype, "bordered");
165
- c([
174
+ p([
166
175
  h({ type: String, reflect: !0 })
167
176
  ], i.prototype, "download");
168
- c([
177
+ p([
169
178
  u()
170
179
  ], i.prototype, "_sortColumn");
171
- c([
180
+ p([
172
181
  u()
173
182
  ], i.prototype, "_sortDirection");
174
- c([
183
+ p([
175
184
  u()
176
185
  ], i.prototype, "_captionText");
177
186
  customElements.get("nys-table") || customElements.define("nys-table", i);
@@ -1 +1 @@
1
- {"version":3,"file":"nys-table.js","sources":["../src/nys-table.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-table.scss?inline\";\n\nlet componentIdCounter = 0;\n\n/**\n * `<nys-table>` is a responsive table component that can display native HTML tables,\n * supports striped and bordered styling, sortable columns, and CSV download.\n *\n * @slot - Accepts a `<table>` element. Only the first table is rendered.\n *\n * @fires nys-click - Fired when the download button or sortable headers are clicked.\n *\n * @method downloadFile - Triggers download of the CSV file if `download` is set.\n */\nexport class NysTable extends LitElement {\n static styles = unsafeCSS(styles);\n\n @property({ type: String, reflect: true }) id = \"\";\n @property({ type: String, reflect: true }) name = \"\";\n @property({ type: Boolean, reflect: true }) striped = false;\n @property({ type: Boolean, reflect: true }) sortable = false;\n @property({ type: Boolean, reflect: true }) bordered = false;\n @property({ type: String, reflect: true }) download = \"\";\n\n @state() private _sortColumn: number | null = null;\n @state() private _sortDirection: \"asc\" | \"desc\" | \"none\" = \"none\";\n @state() private _captionText = \"\";\n\n /**************** Lifecycle Methods ****************/\n constructor() {\n super();\n }\n\n // Generate a unique ID if one is not provided\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-table-${Date.now()}-${componentIdCounter++}`;\n }\n }\n\n /******************** Functions ********************/\n // Placeholder for generic functions (component-specific)\n\n firstUpdated() {\n const slot = this.shadowRoot?.querySelector(\"slot\");\n slot?.addEventListener(\"slotchange\", () => this._handleSlotChange());\n this._handleSlotChange();\n }\n\n _handleSlotChange() {\n const slot = this.shadowRoot?.querySelector(\n \"slot\",\n ) as HTMLSlotElement | null;\n const container = this.shadowRoot?.querySelector(\n \".nys-table\",\n ) as HTMLElement | null;\n if (!slot || !container) return;\n\n container.innerHTML = \"\";\n\n const assigned = slot.assignedElements({ flatten: true });\n\n assigned.forEach((node) => {\n if (node.tagName === \"TABLE\") {\n const table = node.cloneNode(true) as HTMLTableElement;\n this._normalizeTable(table);\n if (this.sortable) {\n this._addSortIcons(table);\n }\n container.appendChild(table);\n }\n });\n }\n\n _normalizeTable(table: HTMLTableElement) {\n const hasThead = table.querySelector(\"thead\");\n const hasTbody = table.querySelector(\"tbody\");\n\n if (hasThead && hasTbody) return;\n\n // Pull caption first\n let caption = table.querySelector(\n \"caption\",\n ) as HTMLTableCaptionElement | null;\n\n // Save caption text if it exists\n if (caption?.textContent?.trim()) {\n this._captionText = caption.textContent.trim();\n } else {\n this._captionText = \"\";\n }\n\n // Collect all rows\n const rows = Array.from(table.querySelectorAll(\"tr\"));\n if (rows.length === 0) return;\n\n const thead = document.createElement(\"thead\");\n const tbody = document.createElement(\"tbody\");\n\n // Find first row containing th\n const headerRowIndex = rows.findIndex((r) => r.querySelector(\"th\"));\n\n if (headerRowIndex === -1) {\n rows.forEach((r) => tbody.appendChild(r));\n } else {\n const headerRow = rows[headerRowIndex];\n\n // Wrap text nodes in <p> for each <th>\n headerRow.querySelectorAll(\"th\").forEach((th) => {\n // Only wrap text nodes, leave icons or other children\n Array.from(th.childNodes).forEach((node) => {\n if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {\n const p = document.createElement(\"p\");\n p.textContent = node.textContent;\n th.replaceChild(p, node);\n }\n });\n });\n\n thead.appendChild(headerRow);\n\n // Move remaining rows to tbody\n rows.forEach((r, i) => {\n if (i !== headerRowIndex) tbody.appendChild(r);\n });\n }\n\n // Wipe original table content\n table.innerHTML = \"\";\n\n // Handle caption and sortable message\n if (this.sortable) {\n if (!caption) {\n caption = document.createElement(\"caption\");\n caption.style.padding = \"0\";\n }\n\n const srOnly = document.createElement(\"span\");\n srOnly.setAttribute(\"class\", \"sr-only\");\n srOnly.textContent = \"Column headers with buttons are sortable.\";\n\n caption.appendChild(srOnly);\n }\n\n if (caption) table.appendChild(caption);\n\n table.appendChild(thead);\n table.appendChild(tbody);\n }\n\n _addSortIcons(table: HTMLTableElement) {\n const ths = Array.from(table.querySelectorAll(\"thead th\"));\n if (ths.length === 0) return;\n\n ths.forEach((th, index) => {\n // Prevent duplicates on slotchange or re-render\n if (th.querySelector(\"nys-button[part='sort-button']\")) return;\n\n // Get existing text content\n const label = th.textContent?.trim();\n if (!label) return;\n\n // Clear existing content\n th.textContent = \"\";\n\n const button = document.createElement(\"nys-button\");\n button.setAttribute(\"part\", \"sort-button\");\n button.setAttribute(\"variant\", \"ghost\");\n button.setAttribute(\"label\", label);\n button.setAttribute(\"suffixIcon\", \"slotted\");\n button.setAttribute(\"fullWidth\", \"true\");\n\n const icon = document.createElement(\"nys-icon\");\n icon.setAttribute(\"slot\", \"suffix-icon\");\n icon.setAttribute(\"name\", \"height\");\n icon.setAttribute(\"size\", \"24\");\n icon.setAttribute(\"color\", \"var(--nys-color-text-weak, #4a4d4f)\");\n\n button.appendChild(icon);\n\n button.addEventListener(\"nys-click\", (e) => {\n e.stopPropagation();\n this._onSortClick(index, table);\n });\n\n th.appendChild(button);\n });\n }\n\n _updateSortIcons(table: HTMLTableElement) {\n const ths = table.querySelectorAll(\"thead th\");\n\n ths.forEach((th, index) => {\n const button = th.querySelector(\"nys-button[part='sort-button']\");\n const icon = button?.querySelector(\n \"nys-icon[slot='suffix-icon']\",\n ) as HTMLElement | null;\n\n if (!button || !icon) return;\n\n if (index === this._sortColumn) {\n th.classList.add(\"nys-table__sortedcolumn\");\n switch (this._sortDirection) {\n case \"asc\":\n icon.setAttribute(\"name\", \"straight\");\n icon.setAttribute(\"color\", \"var(--nys-color-ink, #1b1b1b)\");\n icon.style.transform = \"rotate(0deg)\";\n th.setAttribute(\"aria-sort\", \"ascending\");\n break;\n case \"desc\":\n icon.setAttribute(\"name\", \"straight\");\n icon.setAttribute(\"color\", \"var(--nys-color-ink, #1b1b1b)\");\n icon.style.transform = \"rotate(180deg)\";\n th.setAttribute(\"aria-sort\", \"descending\");\n break;\n }\n } else {\n // Reset for all other columns\n th.classList.remove(\"nys-table__sortedcolumn\");\n icon.setAttribute(\"name\", \"height\");\n icon.setAttribute(\"color\", \"var(--nys-color-text-weak, #4a4d4f)\");\n icon.style.transform = \"\";\n th.removeAttribute(\"aria-sort\");\n }\n });\n }\n\n _onSortClick(columnIndex: number, table: HTMLTableElement) {\n if (this._sortColumn !== columnIndex) {\n // New column → start with ascending\n this._sortColumn = columnIndex;\n this._sortDirection = \"asc\";\n } else {\n // Same column → toggle\n this._sortDirection = this._sortDirection === \"asc\" ? \"desc\" : \"asc\";\n }\n\n this._updateSortIcons(table);\n this._sortTable(table, columnIndex, this._sortDirection);\n }\n\n _sortTable(\n table: HTMLTableElement,\n columnIndex: number,\n direction: \"asc\" | \"desc\",\n ) {\n const tbody = table.querySelector(\"tbody\");\n if (!tbody) return;\n\n const rows = Array.from(tbody.querySelectorAll(\"tr\"));\n\n rows.sort((a, b) => {\n const aText = a.children[columnIndex]?.textContent?.trim() ?? \"\";\n const bText = b.children[columnIndex]?.textContent?.trim() ?? \"\";\n\n const aNum = Number(aText);\n const bNum = Number(bText);\n\n let result;\n\n if (!isNaN(aNum) && !isNaN(bNum)) {\n result = aNum - bNum;\n } else {\n result = aText.localeCompare(bText);\n }\n\n return direction === \"asc\" ? result : -result;\n });\n\n // Re-append sorted rows\n rows.forEach((r) => tbody.appendChild(r));\n this._updateSortedColumnStyles(table);\n }\n\n _updateSortedColumnStyles(table: HTMLTableElement) {\n const rows = table.querySelectorAll(\"tbody tr\");\n\n rows.forEach((row) => {\n Array.from(row.children).forEach((cell, index) => {\n if (index === this._sortColumn) {\n cell.classList.add(\"nys-table__sortedcolumn\");\n } else {\n cell.classList.remove(\"nys-table__sortedcolumn\");\n }\n });\n });\n }\n\n downloadFile() {\n // read file from this.download and trigger download\n const link = document.createElement(\"a\");\n link.href = this.download;\n link.download = this.download.split(\"/\").pop() || \"table-data.csv\";\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n\n /****************** Event Handlers ******************/\n // Placeholder for event handlers if needed\n\n render() {\n return html`\n <div class=\"nys-table\">\n <div class=\"table-container\"></div>\n </div>\n ${this.download\n ? html` <nys-button\n id=\"${this.id}-download-button\"\n label=\"Download table\"\n aria-label=${this._captionText\n ? `Download ${this._captionText}`\n : \"Download table\"}\n size=\"sm\"\n variant=\"outline\"\n prefixIcon=\"download\"\n @nys-click=${this.downloadFile}\n ></nys-button>`\n : \"\"}\n <slot style=\"display:none\"></slot>\n `;\n }\n}\n\nif (!customElements.get(\"nys-table\")) {\n customElements.define(\"nys-table\", NysTable);\n}\n"],"names":["componentIdCounter","_NysTable","LitElement","slot","container","node","table","hasThead","hasTbody","caption","rows","thead","tbody","headerRowIndex","r","headerRow","th","p","i","srOnly","ths","index","label","button","icon","e","columnIndex","direction","a","b","aText","bText","aNum","bNum","result","row","cell","link","html","unsafeCSS","styles","NysTable","__decorateClass","property","state"],"mappings":"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"}
1
+ {"version":3,"file":"nys-table.js","sources":["../src/nys-table.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-table.scss?inline\";\n\nlet componentIdCounter = 0;\n\n/**\n * `<nys-table>` is a responsive table component that can display native HTML tables,\n * supports striped and bordered styling, sortable columns, and CSV download.\n *\n * @slot - Accepts a `<table>` element. Only the first table is rendered.\n *\n * @fires nys-click - Fired when the download button or sortable headers are clicked.\n *\n * @method downloadFile - Triggers download of the CSV file if `download` is set.\n */\nexport class NysTable extends LitElement {\n static styles = unsafeCSS(styles);\n\n @property({ type: String, reflect: true }) id = \"\";\n @property({ type: String, reflect: true }) name = \"\";\n @property({ type: Boolean, reflect: true }) striped = false;\n @property({ type: Boolean, reflect: true }) sortable = false;\n @property({ type: Boolean, reflect: true }) bordered = false;\n @property({ type: String, reflect: true }) download = \"\";\n\n @state() private _sortColumn: number | null = null;\n @state() private _sortDirection: \"asc\" | \"desc\" | \"none\" = \"none\";\n @state() private _captionText = \"\";\n\n /**************** Lifecycle Methods ****************/\n constructor() {\n super();\n }\n\n // Generate a unique ID if one is not provided\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-table-${Date.now()}-${componentIdCounter++}`;\n }\n }\n\n /******************** Functions ********************/\n // Placeholder for generic functions (component-specific)\n\n firstUpdated() {\n const slot = this.shadowRoot?.querySelector(\"slot\");\n slot?.addEventListener(\"slotchange\", () => this._handleSlotChange());\n this._handleSlotChange();\n }\n\n _handleSlotChange() {\n const slot = this.shadowRoot?.querySelector(\n \"slot\",\n ) as HTMLSlotElement | null;\n const container = this.shadowRoot?.querySelector(\n \".table-container\",\n ) as HTMLElement | null;\n\n if (!slot || !container) return;\n\n container.innerHTML = \"\";\n\n const assigned = slot.assignedElements({ flatten: true });\n const tableEl = assigned.find((el) => el.tagName === \"TABLE\") as\n | HTMLTableElement\n | undefined;\n\n if (!tableEl) return;\n\n const table = tableEl.cloneNode(true) as HTMLTableElement;\n\n this._normalizeTableDOM(table);\n\n if (this.sortable) {\n this._addSortIcons(table);\n }\n\n container.appendChild(table);\n }\n\n _normalizeTableDOM(table: HTMLTableElement) {\n const hasThead = table.querySelector(\"thead\");\n const hasTbody = table.querySelector(\"tbody\");\n\n if (hasThead && hasTbody) return;\n\n // Pull caption first\n const caption = table.querySelector(\n \"caption\",\n ) as HTMLTableCaptionElement | null;\n\n // Collect all rows\n const rows = Array.from(table.querySelectorAll(\"tr\"));\n if (rows.length === 0) return;\n\n const thead = document.createElement(\"thead\");\n const tbody = document.createElement(\"tbody\");\n\n // Find first row containing th\n const headerRowIndex = rows.findIndex((r) => r.querySelector(\"th\"));\n\n if (headerRowIndex === -1) {\n rows.forEach((r) => tbody.appendChild(r));\n } else {\n const headerRow = rows[headerRowIndex];\n\n // Wrap text nodes in <p> for each <th>\n headerRow.querySelectorAll(\"th\").forEach((th) => {\n // Only wrap text nodes, leave icons or other children\n Array.from(th.childNodes).forEach((node) => {\n if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) {\n const p = document.createElement(\"p\");\n p.textContent = node.textContent;\n th.replaceChild(p, node);\n }\n });\n });\n\n thead.appendChild(headerRow);\n\n // Move remaining rows to tbody\n rows.forEach((r, i) => {\n if (i !== headerRowIndex) tbody.appendChild(r);\n });\n }\n\n // Wipe original table content\n table.innerHTML = \"\";\n\n // Handle caption and sortable message\n if (caption) {\n table.appendChild(caption);\n }\n\n if (this.sortable) {\n const srOnly = document.createElement(\"span\");\n srOnly.setAttribute(\"class\", \"sr-only\");\n srOnly.textContent = \"Column headers with buttons are sortable.\";\n\n if (caption) {\n caption.appendChild(srOnly);\n } else {\n const nextCaption = document.createElement(\"caption\");\n nextCaption.appendChild(srOnly);\n table.appendChild(nextCaption);\n }\n }\n\n table.appendChild(thead);\n table.appendChild(tbody);\n }\n\n willUpdate() {\n const table = Array.from(this.children).find(\n (el) => el.tagName === \"TABLE\",\n ) as HTMLTableElement | undefined;\n\n if (!table) return;\n\n const caption = table.querySelector(\"caption\");\n const nextCaption = caption?.textContent?.trim() ?? \"\";\n\n if (this._captionText !== nextCaption) {\n this._captionText = nextCaption;\n }\n }\n\n _addSortIcons(table: HTMLTableElement) {\n const ths = Array.from(table.querySelectorAll(\"thead th\"));\n if (ths.length === 0) return;\n\n ths.forEach((th, index) => {\n // Prevent duplicates on slotchange or re-render\n if (th.querySelector(\"nys-button[part='sort-button']\")) return;\n\n // Get existing text content\n const label = th.textContent?.trim();\n if (!label) return;\n\n // Clear existing content\n th.textContent = \"\";\n\n const button = document.createElement(\"nys-button\");\n button.setAttribute(\"part\", \"sort-button\");\n button.setAttribute(\"variant\", \"ghost\");\n button.setAttribute(\"label\", label);\n button.setAttribute(\"suffixIcon\", \"slotted\");\n button.setAttribute(\"fullWidth\", \"true\");\n\n const icon = document.createElement(\"nys-icon\");\n icon.setAttribute(\"slot\", \"suffix-icon\");\n icon.setAttribute(\"name\", \"height\");\n icon.setAttribute(\"size\", \"24\");\n icon.setAttribute(\"color\", \"var(--nys-color-text-weak, #4a4d4f)\");\n\n button.appendChild(icon);\n\n button.addEventListener(\"nys-click\", (e) => {\n e.stopPropagation();\n this._onSortClick(index, table);\n });\n\n th.appendChild(button);\n });\n }\n\n _updateSortIcons(table: HTMLTableElement) {\n const ths = table.querySelectorAll(\"thead th\");\n\n ths.forEach((th, index) => {\n const button = th.querySelector(\"nys-button[part='sort-button']\");\n const icon = button?.querySelector(\n \"nys-icon[slot='suffix-icon']\",\n ) as HTMLElement | null;\n\n if (!button || !icon) return;\n\n if (index === this._sortColumn) {\n th.classList.add(\"nys-table__sortedcolumn\");\n switch (this._sortDirection) {\n case \"asc\":\n icon.setAttribute(\"name\", \"straight\");\n icon.setAttribute(\"color\", \"var(--nys-color-ink, #1b1b1b)\");\n icon.style.transform = \"rotate(0deg)\";\n th.setAttribute(\"aria-sort\", \"ascending\");\n break;\n case \"desc\":\n icon.setAttribute(\"name\", \"straight\");\n icon.setAttribute(\"color\", \"var(--nys-color-ink, #1b1b1b)\");\n icon.style.transform = \"rotate(180deg)\";\n th.setAttribute(\"aria-sort\", \"descending\");\n break;\n }\n } else {\n // Reset for all other columns\n th.classList.remove(\"nys-table__sortedcolumn\");\n icon.setAttribute(\"name\", \"height\");\n icon.setAttribute(\"color\", \"var(--nys-color-text-weak, #4a4d4f)\");\n icon.style.transform = \"\";\n th.removeAttribute(\"aria-sort\");\n }\n });\n }\n\n _onSortClick(columnIndex: number, table: HTMLTableElement) {\n if (this._sortColumn !== columnIndex) {\n // New column → start with ascending\n this._sortColumn = columnIndex;\n this._sortDirection = \"asc\";\n } else {\n // Same column → toggle\n this._sortDirection = this._sortDirection === \"asc\" ? \"desc\" : \"asc\";\n }\n\n this._updateSortIcons(table);\n this._sortTable(table, columnIndex, this._sortDirection);\n }\n\n _sortTable(\n table: HTMLTableElement,\n columnIndex: number,\n direction: \"asc\" | \"desc\",\n ) {\n const tbody = table.querySelector(\"tbody\");\n if (!tbody) return;\n\n const rows = Array.from(tbody.querySelectorAll(\"tr\"));\n\n rows.sort((a, b) => {\n const aText = a.children[columnIndex]?.textContent?.trim() ?? \"\";\n const bText = b.children[columnIndex]?.textContent?.trim() ?? \"\";\n\n const aNum = Number(aText);\n const bNum = Number(bText);\n\n let result;\n\n if (!isNaN(aNum) && !isNaN(bNum)) {\n result = aNum - bNum;\n } else {\n result = aText.localeCompare(bText);\n }\n\n return direction === \"asc\" ? result : -result;\n });\n\n // Re-append sorted rows\n rows.forEach((r) => tbody.appendChild(r));\n this._updateSortedColumnStyles(table);\n }\n\n _updateSortedColumnStyles(table: HTMLTableElement) {\n const rows = table.querySelectorAll(\"tbody tr\");\n\n rows.forEach((row) => {\n Array.from(row.children).forEach((cell, index) => {\n if (index === this._sortColumn) {\n cell.classList.add(\"nys-table__sortedcolumn\");\n } else {\n cell.classList.remove(\"nys-table__sortedcolumn\");\n }\n });\n });\n }\n\n downloadFile() {\n // read file from this.download and trigger download\n const link = document.createElement(\"a\");\n link.href = this.download;\n link.download = this.download.split(\"/\").pop() || \"table-data.csv\";\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n\n /****************** Event Handlers ******************/\n // Placeholder for event handlers if needed\n\n render() {\n return html`\n <div class=\"nys-table\">\n <div class=\"table-container\"></div>\n </div>\n ${this.download\n ? html` <nys-button\n id=\"${this.id}-download-button\"\n label=\"Download table\"\n aria-label=${this._captionText\n ? `Download ${this._captionText}`\n : \"Download table\"}\n size=\"sm\"\n variant=\"outline\"\n prefixIcon=\"download\"\n @nys-click=${this.downloadFile}\n ></nys-button>`\n : \"\"}\n <slot style=\"display:none\"></slot>\n `;\n }\n}\n\nif (!customElements.get(\"nys-table\")) {\n customElements.define(\"nys-table\", NysTable);\n}\n"],"names":["componentIdCounter","_NysTable","LitElement","slot","container","tableEl","el","table","hasThead","hasTbody","caption","rows","thead","tbody","headerRowIndex","r","headerRow","th","node","p","i","srOnly","nextCaption","ths","index","label","button","icon","e","columnIndex","direction","a","b","aText","bText","aNum","bNum","result","row","cell","link","html","unsafeCSS","styles","NysTable","__decorateClass","property","state"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nysds/nys-table",
3
- "version": "1.13.0",
3
+ "version": "1.13.1",
4
4
  "description": "The Table component from the NYS Design System.",
5
5
  "module": "dist/nys-table.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,8 +26,8 @@
26
26
  "lit": "^3.3.1",
27
27
  "typescript": "^5.9.3",
28
28
  "vite": "^7.1.12",
29
- "@nysds/nys-button": "^1.13.0",
30
- "@nysds/nys-icon": "^1.13.0"
29
+ "@nysds/nys-button": "^1.13.1",
30
+ "@nysds/nys-icon": "^1.13.1"
31
31
  },
32
32
  "keywords": [
33
33
  "new-york-state",