@ansiversa/components 0.0.87 → 0.0.88

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ansiversa/components",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
4
4
  "description": "Shared UI components and layouts for the Ansiversa ecosystem",
5
5
  "type": "module",
6
6
  "exports": {
package/src/AvTable.astro CHANGED
@@ -5,14 +5,90 @@ interface Props {
5
5
  dense?: boolean;
6
6
  stickyHeader?: boolean;
7
7
  scroll?: boolean; // enable x-scroll container
8
+ sortKey?: string;
9
+ sortDir?: "asc" | "desc";
8
10
  }
9
11
 
10
- const { caption, className = "", dense = false, stickyHeader = false, scroll = true } = Astro.props as Props;
12
+ const {
13
+ caption,
14
+ className = "",
15
+ dense = false,
16
+ stickyHeader = false,
17
+ scroll = true,
18
+ sortKey,
19
+ sortDir,
20
+ } = Astro.props as Props;
21
+
22
+ const normalizedSortDir = sortDir === "asc" || sortDir === "desc" ? sortDir : undefined;
11
23
  ---
12
24
 
13
- <div class={`av-table ${scroll ? "av-table--scroll" : ""} ${dense ? "av-table--dense" : ""} ${stickyHeader ? "av-table--sticky" : ""} ${className}`}>
25
+ <div
26
+ class={`av-table ${scroll ? "av-table--scroll" : ""} ${dense ? "av-table--dense" : ""} ${stickyHeader ? "av-table--sticky" : ""} ${className}`}
27
+ data-sort-key={sortKey}
28
+ data-sort-dir={normalizedSortDir}
29
+ >
14
30
  <table class="av-table__table">
15
31
  {caption ? <caption class="av-table__caption">{caption}</caption> : null}
16
32
  <slot />
17
33
  </table>
18
34
  </div>
35
+
36
+ <script define:vars={{ initialSortKey: sortKey ?? "", initialSortDir: normalizedSortDir ?? "" }}>
37
+ (() => {
38
+ const root = document.currentScript?.closest(".av-table");
39
+ if (!root) return;
40
+
41
+ const controls = Array.from(root.querySelectorAll("[data-av-sort]"));
42
+ if (!controls.length) return;
43
+
44
+ function applySortState(key, dir) {
45
+ root.dataset.sortKey = key || "";
46
+ root.dataset.sortDir = dir || "";
47
+
48
+ controls.forEach((control) => {
49
+ const sortKey = control.getAttribute("data-av-sort") || "";
50
+ const th = control.closest("th");
51
+ const isActive = sortKey && sortKey === key;
52
+
53
+ if (th) {
54
+ th.setAttribute(
55
+ "aria-sort",
56
+ isActive ? (dir === "desc" ? "descending" : "ascending") : "none"
57
+ );
58
+ }
59
+
60
+ control.classList.toggle("av-table__sort--active", isActive);
61
+ control.classList.toggle("av-table__sort--asc", isActive && dir === "asc");
62
+ control.classList.toggle("av-table__sort--desc", isActive && dir === "desc");
63
+ });
64
+ }
65
+
66
+ const startKey = root.dataset.sortKey || initialSortKey;
67
+ const startDir = root.dataset.sortDir || initialSortDir;
68
+ if (startKey && startDir) {
69
+ applySortState(startKey, startDir);
70
+ }
71
+
72
+ controls.forEach((control) => {
73
+ control.addEventListener("click", (event) => {
74
+ const key = control.getAttribute("data-av-sort") || "";
75
+ if (!key) return;
76
+
77
+ const defaultDir =
78
+ (control.getAttribute("data-av-sort-default") || "asc").toLowerCase() === "desc"
79
+ ? "desc"
80
+ : "asc";
81
+ const currentKey = root.dataset.sortKey || "";
82
+ const currentDir = root.dataset.sortDir || "";
83
+ const nextDir = key === currentKey ? (currentDir === "desc" ? "asc" : "desc") : defaultDir;
84
+
85
+ applySortState(key, nextDir);
86
+ root.dispatchEvent(new CustomEvent("av-sort", { bubbles: true, detail: { key, dir: nextDir } }));
87
+
88
+ if (control.tagName === "BUTTON") {
89
+ event.preventDefault();
90
+ }
91
+ });
92
+ });
93
+ })();
94
+ </script>
@@ -1126,6 +1126,37 @@
1126
1126
  white-space: nowrap;
1127
1127
  }
1128
1128
 
1129
+ .av-table__sort {
1130
+ display: inline-flex;
1131
+ align-items: center;
1132
+ gap: 0.35rem;
1133
+ color: inherit;
1134
+ background: none;
1135
+ border: 0;
1136
+ padding: 0;
1137
+ font: inherit;
1138
+ cursor: pointer;
1139
+ }
1140
+
1141
+ .av-table__sort::after {
1142
+ content: "";
1143
+ display: inline-block;
1144
+ width: 0;
1145
+ height: 0;
1146
+ border-left: 4px solid transparent;
1147
+ border-right: 4px solid transparent;
1148
+ border-top: 6px solid var(--ans-muted);
1149
+ transform: translateY(1px);
1150
+ }
1151
+
1152
+ .av-table__sort--active::after {
1153
+ border-top-color: var(--ans-text);
1154
+ }
1155
+
1156
+ .av-table__sort--asc::after {
1157
+ transform: rotate(180deg);
1158
+ }
1159
+
1129
1160
  .av-table--sticky thead th {
1130
1161
  position: sticky;
1131
1162
  top: 0;