@serkonda7/solid-components 0.0.2 → 0.0.3

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.md CHANGED
@@ -14,31 +14,21 @@ bun install @serkonda7/solid-components solid-js
14
14
 
15
15
  ## 🚀 Usage
16
16
  ```tsx
17
- import { /* Component */ } from 'solid-components'
17
+ import { DataTable } from '@serkonda7/solid-components'
18
18
  import '@serkonda7/solid-components/styles.css'
19
- ```
20
-
21
- These components are available:
22
- - `DataTable` - generic table with optional sorting, row selection, actions, loading, and empty states.
23
-
24
-
25
- ## Components
26
- ### DataTable
27
- The table does not fetch data or perform navigation.
28
- Consumers provide the rows, cell rendering, and callbacks:
29
19
 
30
- ```tsx
31
20
  <DataTable
32
21
  rows={items}
33
22
  getRowId={(item) => item.id}
34
- columns={[{ key: 'name', label: 'Name', sortable: true }]}
35
- sortKey={sortKey}
36
- sortDirection={sortDirection}
37
- onSort={setSortKey}
38
- getCell={(item, column) => column.key === 'name' ? item.name : null}
23
+ columns={[{ key: 'name', label: 'Name' }]}
39
24
  />
40
25
  ```
41
26
 
27
+ These components are available:
28
+ - `DataTable` - generic table with optional sorting, row selection, actions, loading, empty states, and column customizer.
29
+
30
+ For the full API, examples, and agent notes, see [docs.md](docs.md).
31
+
42
32
 
43
33
  ## 📜 License
44
34
  This repo is licensed under the [MIT License](LICENSE.txt).
@@ -7,6 +7,10 @@ export interface DataTableColumn<TRow> {
7
7
  class?: string;
8
8
  ariaLabel?: string;
9
9
  getValue?: (row: TRow) => JSX.Element;
10
+ /** Whether the column is visible by default when the column customizer is used. Defaults to `true`. */
11
+ defaultVisible?: boolean;
12
+ /** Whether the column can be hidden via the column customizer. Defaults to `true`. Non-toggleable columns are always shown. */
13
+ toggleable?: boolean;
10
14
  }
11
15
  export interface DataTableProps<TRow> {
12
16
  rows: TRow[] | Accessor<TRow[]>;
@@ -20,6 +24,14 @@ export interface DataTableProps<TRow> {
20
24
  sortKey?: string | Accessor<string | undefined>;
21
25
  sortDirection?: DataTableSortDirection | Accessor<DataTableSortDirection>;
22
26
  onSort?: (key: string) => void;
27
+ visibleColumns?: string[] | Accessor<string[]>;
28
+ defaultVisibleColumns?: string[];
29
+ onVisibleColumnsChange?: (visibleKeys: string[]) => void;
30
+ showColumnCustomizer?: boolean | Accessor<boolean>;
31
+ columnCustomizerLabel?: string;
32
+ columnCustomizerTitle?: string;
33
+ columnCustomizerShowAllLabel?: string;
34
+ columnCustomizerResetLabel?: string;
23
35
  loading?: boolean | Accessor<boolean>;
24
36
  empty?: boolean | Accessor<boolean>;
25
37
  loadingContent?: JSX.Element;
package/dist/index.js CHANGED
@@ -1,49 +1,110 @@
1
1
  import { className as e, createComponent as t, delegateEvents as n, effect as r, insert as i, memo as a, setAttribute as o, template as s, use as c } from "solid-js/web";
2
- import { For as l, Show as u, createEffect as d, createMemo as f } from "solid-js";
2
+ import { For as l, Show as u, createEffect as d, createMemo as f, createSignal as p, createUniqueId as m, onCleanup as h } from "solid-js";
3
3
  //#region src/data-table.tsx
4
- var p = /*#__PURE__*/ s("<th class=data-table-checkbox><span class=data-table-visually-hidden>Select rows</span><input type=checkbox>"), m = /*#__PURE__*/ s("<th>Actions"), h = /*#__PURE__*/ s("<div><table class=data-table><thead><tr></tr></thead><tbody>"), g = /*#__PURE__*/ s("<button type=button class=data-table-sort>"), _ = /*#__PURE__*/ s("<th>"), v = /*#__PURE__*/ s("<td class=data-table-checkbox><input type=checkbox>"), y = /*#__PURE__*/ s("<td>"), b = /*#__PURE__*/ s("<tr>"), x = /*#__PURE__*/ s("<p class=data-table-status>Loading…"), S = /*#__PURE__*/ s("<p class=data-table-status>No results.");
5
- function C(e) {
4
+ var g = /*#__PURE__*/ s("<div role=dialog class=data-table-customizer-panel><p class=data-table-customizer-title></p><ul class=data-table-customizer-list></ul><div class=data-table-customizer-actions><button type=button></button><button type=button>"), _ = /*#__PURE__*/ s("<div class=data-table-toolbar><div class=data-table-customizer><button type=button class=data-table-customizer-button aria-haspopup=dialog><span aria-hidden=true>⚙</span> "), v = /*#__PURE__*/ s("<th class=data-table-checkbox><span class=data-table-visually-hidden>Select rows</span><input type=checkbox>"), y = /*#__PURE__*/ s("<th>Actions"), b = /*#__PURE__*/ s("<div><div class=data-table-scroll><table class=data-table><thead><tr></tr></thead><tbody>"), x = /*#__PURE__*/ s("<li><label class=data-table-customizer-option><input type=checkbox>"), S = /*#__PURE__*/ s("<button type=button class=data-table-sort>"), C = /*#__PURE__*/ s("<th>"), ee = /*#__PURE__*/ s("<td class=data-table-checkbox><input type=checkbox>"), w = /*#__PURE__*/ s("<td>"), T = /*#__PURE__*/ s("<tr>"), E = /*#__PURE__*/ s("<p class=data-table-status>Loading…"), D = /*#__PURE__*/ s("<p class=data-table-status>No results.");
5
+ function O(e) {
6
6
  return typeof e == "function" ? e() : e;
7
7
  }
8
- function w(n) {
9
- let s = f(() => C(n.rows)), w = () => C(n.loading ?? !1), T = () => C(n.selected ?? []), E = () => C(n.sortKey ?? void 0), D = () => C(n.sortDirection ?? "asc"), O = () => n.selected !== void 0 && n.onSelectionChange !== void 0, k = () => {
10
- let e = T();
8
+ function k(n) {
9
+ let s = f(() => O(n.rows)), k = () => O(n.loading ?? !1), A = () => O(n.selected ?? []), j = () => O(n.sortKey ?? void 0), M = () => O(n.sortDirection ?? "asc"), N = () => O(n.showColumnCustomizer ?? !1), P = () => n.selected !== void 0 && n.onSelectionChange !== void 0, F = () => {
10
+ let e = A();
11
11
  return s().length > 0 && s().every((t) => e.includes(n.getRowId(t)));
12
- }, A;
12
+ }, I = f(() => n.defaultVisibleColumns ?? n.columns.filter((e) => e.defaultVisible !== !1).map((e) => e.key)), [L, R] = p(void 0), z = () => n.visibleColumns === void 0 ? L() ?? I() : O(n.visibleColumns), B = f(() => new Set(z())), V = f(() => n.columns.filter((e) => e.toggleable === !1 || B().has(e.key))), H = f(() => n.columns.filter((e) => e.toggleable !== !1)), U = f(() => H().map((e) => e.key));
13
+ function W(e) {
14
+ let t = new Map(n.columns.map((e, t) => [e.key, t]));
15
+ return [...e].sort((e, n) => (t.get(e) ?? 0) - (t.get(n) ?? 0));
16
+ }
17
+ function G(e) {
18
+ let t = W(e);
19
+ n.visibleColumns === void 0 && R(t), n.onVisibleColumnsChange?.(t);
20
+ }
21
+ function K(e, t) {
22
+ let n = new Set(z());
23
+ t ? n.add(e) : n.delete(e), G(U().filter((e) => n.has(e)));
24
+ }
25
+ function q() {
26
+ G(U());
27
+ }
28
+ function J() {
29
+ G(I());
30
+ }
31
+ let Y = `${m()}-panel`, [X, Z] = p(!1), Q;
32
+ d(() => {
33
+ if (!X() || !N()) return;
34
+ function e(e) {
35
+ Q && !Q.contains(e.target) && Z(!1);
36
+ }
37
+ function t(e) {
38
+ e.key === "Escape" && Z(!1);
39
+ }
40
+ document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), h(() => {
41
+ document.removeEventListener("pointerdown", e), document.removeEventListener("keydown", t);
42
+ });
43
+ });
44
+ let $;
13
45
  d(() => {
14
- A && (A.indeterminate = T().length > 0 && !k());
46
+ $ && ($.indeterminate = A().length > 0 && !F());
15
47
  });
16
- function j(e) {
48
+ function te(e) {
17
49
  n.onSelectionChange?.(e ? s().map(n.getRowId) : []);
18
50
  }
19
- function M(e, t) {
20
- let r = n.getRowId(e), i = T(), a = t ? [...i, r] : i.filter((e) => e !== r);
51
+ function ne(e, t) {
52
+ let r = n.getRowId(e), i = A(), a = t ? [...i, r] : i.filter((e) => e !== r);
21
53
  n.onSelectionChange?.(a);
22
54
  }
23
- function N(e) {
24
- return T().includes(n.getRowId(e));
55
+ function re(e) {
56
+ return A().includes(n.getRowId(e));
57
+ }
58
+ function ie(e) {
59
+ return e.toggleable === !1 || B().has(e.key);
25
60
  }
26
- function P(e, t) {
61
+ function ae(e, t) {
27
62
  return n.getCell?.(e, t) ?? t.getValue?.(e) ?? null;
28
63
  }
29
64
  return (() => {
30
- var d = h(), f = d.firstChild.firstChild, T = f.firstChild, F = f.nextSibling;
31
- return i(T, t(u, {
65
+ var d = b(), f = d.firstChild, p = f.firstChild.firstChild, m = p.firstChild, h = p.nextSibling;
66
+ return i(d, t(u, {
67
+ get when() {
68
+ return N();
69
+ },
70
+ get children() {
71
+ var e = _(), a = e.firstChild, s = a.firstChild;
72
+ s.firstChild.nextSibling;
73
+ var d = Q;
74
+ return typeof d == "function" ? c(d, a) : Q = a, s.$$click = () => Z((e) => !e), o(s, "aria-controls", Y), i(s, () => n.columnCustomizerLabel ?? "Columns", null), i(a, t(u, {
75
+ get when() {
76
+ return X();
77
+ },
78
+ get children() {
79
+ var e = g(), a = e.firstChild, s = a.nextSibling, c = s.nextSibling.firstChild, u = c.nextSibling;
80
+ return o(e, "id", Y), i(a, () => n.columnCustomizerTitle ?? "Shown columns"), i(s, t(l, {
81
+ get each() {
82
+ return n.columns;
83
+ },
84
+ children: (e) => (() => {
85
+ var t = x(), n = t.firstChild, a = n.firstChild;
86
+ return a.addEventListener("change", (t) => K(e.key, t.currentTarget.checked)), i(n, () => e.ariaLabel ?? e.label, null), r(() => a.disabled = e.toggleable === !1), r(() => a.checked = ie(e)), t;
87
+ })()
88
+ })), c.$$click = q, i(c, () => n.columnCustomizerShowAllLabel ?? "Show all"), u.$$click = J, i(u, () => n.columnCustomizerResetLabel ?? "Reset"), r(() => o(e, "aria-label", n.columnCustomizerTitle ?? "Customize columns")), e;
89
+ }
90
+ }), null), r(() => o(s, "aria-expanded", X())), e;
91
+ }
92
+ }), f), i(m, t(u, {
32
93
  get when() {
33
- return O();
94
+ return P();
34
95
  },
35
96
  get children() {
36
- var e = p(), t = e.firstChild.nextSibling;
37
- t.addEventListener("change", (e) => j(e.currentTarget.checked));
38
- var i = A;
39
- return typeof i == "function" ? c(i, t) : A = t, r(() => o(t, "aria-label", n.selectionLabel ?? "Select all rows")), r(() => t.checked = k()), e;
97
+ var e = v(), t = e.firstChild.nextSibling;
98
+ t.addEventListener("change", (e) => te(e.currentTarget.checked));
99
+ var i = $;
100
+ return typeof i == "function" ? c(i, t) : $ = t, r(() => o(t, "aria-label", n.selectionLabel ?? "Select all rows")), r(() => t.checked = F()), e;
40
101
  }
41
- }), null), i(T, t(l, {
102
+ }), null), i(m, t(l, {
42
103
  get each() {
43
- return n.columns;
104
+ return V();
44
105
  },
45
106
  children: (s) => (() => {
46
- var c = _();
107
+ var c = C();
47
108
  return i(c, t(u, {
48
109
  get when() {
49
110
  return a(() => !!s.sortable)() && n.onSort;
@@ -52,76 +113,76 @@ function w(n) {
52
113
  return s.label;
53
114
  },
54
115
  get children() {
55
- var e = g();
116
+ var e = S();
56
117
  return e.$$click = () => n.onSort?.(s.key), i(e, () => s.label, null), i(e, (() => {
57
- var e = a(() => E() === s.key);
58
- return () => e() ? D() === "asc" ? " ▲" : " ▼" : "";
118
+ var e = a(() => j() === s.key);
119
+ return () => e() ? M() === "asc" ? " ▲" : " ▼" : "";
59
120
  })(), null), e;
60
121
  }
61
122
  })), r((t) => {
62
- var n = s.class, r = s.sortable ? E() === s.key ? D() === "asc" ? "ascending" : "descending" : "none" : void 0;
123
+ var n = s.class, r = s.sortable ? j() === s.key ? M() === "asc" ? "ascending" : "descending" : "none" : void 0;
63
124
  return n !== t.e && e(c, t.e = n), r !== t.t && o(c, "aria-sort", t.t = r), t;
64
125
  }, {
65
126
  e: void 0,
66
127
  t: void 0
67
128
  }), c;
68
129
  })()
69
- }), null), i(T, t(u, {
130
+ }), null), i(m, t(u, {
70
131
  get when() {
71
132
  return n.rowActions;
72
133
  },
73
134
  get children() {
74
- return m();
135
+ return y();
75
136
  }
76
- }), null), i(F, t(l, {
137
+ }), null), i(h, t(l, {
77
138
  get each() {
78
139
  return s();
79
140
  },
80
141
  children: (a) => (() => {
81
- var s = b();
142
+ var s = T();
82
143
  return i(s, t(u, {
83
144
  get when() {
84
- return O();
145
+ return P();
85
146
  },
86
147
  get children() {
87
- var e = v(), t = e.firstChild;
88
- return t.addEventListener("change", (e) => M(a, e.currentTarget.checked)), r(() => o(t, "aria-label", `Select row ${n.getRowId(a)}`)), r(() => t.checked = N(a)), e;
148
+ var e = ee(), t = e.firstChild;
149
+ return t.addEventListener("change", (e) => ne(a, e.currentTarget.checked)), r(() => o(t, "aria-label", `Select row ${n.getRowId(a)}`)), r(() => t.checked = re(a)), e;
89
150
  }
90
151
  }), null), i(s, t(l, {
91
152
  get each() {
92
- return n.columns;
153
+ return V();
93
154
  },
94
155
  children: (t) => (() => {
95
- var n = y();
96
- return i(n, () => P(a, t)), r(() => e(n, t.class)), n;
156
+ var n = w();
157
+ return i(n, () => ae(a, t)), r(() => e(n, t.class)), n;
97
158
  })()
98
159
  }), null), i(s, t(u, {
99
160
  get when() {
100
161
  return n.rowActions;
101
162
  },
102
163
  get children() {
103
- var e = y();
164
+ var e = w();
104
165
  return i(e, () => n.rowActions?.(a)), e;
105
166
  }
106
167
  }), null), s;
107
168
  })()
108
169
  })), i(d, t(u, {
109
170
  get when() {
110
- return w();
171
+ return k();
111
172
  },
112
173
  get children() {
113
- return n.loadingContent ?? x();
174
+ return n.loadingContent ?? E();
114
175
  }
115
176
  }), null), i(d, t(u, {
116
177
  get when() {
117
- return a(() => !w())() && (n.empty === void 0 ? s().length === 0 : C(n.empty));
178
+ return a(() => !k())() && (n.empty === void 0 ? s().length === 0 : O(n.empty));
118
179
  },
119
180
  get children() {
120
- return n.emptyContent ?? S();
181
+ return n.emptyContent ?? D();
121
182
  }
122
183
  }), null), r(() => e(d, `data-table-wrap${n.class ? ` ${n.class}` : ""}`)), d;
123
184
  })();
124
185
  }
125
186
  n(["click"]);
126
187
  //#endregion
127
- export { w as DataTable };
188
+ export { k as DataTable };
package/dist/styles.css CHANGED
@@ -1,7 +1,95 @@
1
1
  .data-table-wrap {
2
+ /* `color-scheme` is intentionally inherited so `light-dark()` below follows
3
+ the consumer's theme (e.g. the dark example). */
4
+ }
5
+
6
+ .data-table-scroll {
2
7
  overflow-x: auto;
3
8
  }
4
9
 
10
+ .data-table-toolbar {
11
+ display: flex;
12
+ justify-content: flex-end;
13
+ margin-bottom: 0.5rem;
14
+ }
15
+
16
+ .data-table-customizer {
17
+ position: relative;
18
+ }
19
+
20
+ .data-table-customizer-button {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ gap: 0.375rem;
24
+ padding: 0.375rem 0.625rem;
25
+ border: 1px solid
26
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
27
+ border-radius: 0.375rem;
28
+ background: var(
29
+ --data-table-customizer-bg,
30
+ var(--surface, light-dark(#ffffff, #1d2024))
31
+ );
32
+ color: inherit;
33
+ font: inherit;
34
+ cursor: pointer;
35
+ white-space: nowrap;
36
+ }
37
+
38
+ .data-table-customizer-button:hover {
39
+ background: var(
40
+ --data-table-customizer-hover-bg,
41
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
42
+ );
43
+ }
44
+
45
+ .data-table-customizer-panel {
46
+ position: absolute;
47
+ top: calc(100% + 0.375rem);
48
+ right: 0;
49
+ z-index: 20;
50
+ min-width: 12rem;
51
+ padding: 0.75rem;
52
+ border: 1px solid
53
+ var(--data-table-border, var(--border, light-dark(#d0d0d0, #30343a)));
54
+ border-radius: 0.5rem;
55
+ background: var(
56
+ --data-table-panel-bg,
57
+ var(--surface, light-dark(#ffffff, #1d2024))
58
+ );
59
+ box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
60
+ }
61
+
62
+ .data-table-customizer-title {
63
+ margin: 0 0 0.5rem;
64
+ font-weight: 600;
65
+ }
66
+
67
+ .data-table-customizer-list {
68
+ margin: 0;
69
+ padding: 0;
70
+ list-style: none;
71
+ display: grid;
72
+ gap: 0.375rem;
73
+ }
74
+
75
+ .data-table-customizer-option {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 0.5rem;
79
+ cursor: pointer;
80
+ }
81
+
82
+ .data-table-customizer-option:has(input:disabled) {
83
+ opacity: 0.6;
84
+ cursor: not-allowed;
85
+ }
86
+
87
+ .data-table-customizer-actions {
88
+ display: flex;
89
+ gap: 0.5rem;
90
+ margin-top: 0.75rem;
91
+ }
92
+
5
93
  .data-table {
6
94
  width: 100%;
7
95
  max-width: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serkonda7/solid-components",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Reusable components for SolidJS",
5
5
  "license": "MIT",
6
6
  "author": "serkonda7",