@serkonda7/solid-components 0.0.6 → 0.0.7

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
@@ -8,7 +8,7 @@ Reusable components for [SolidJS](https://www.solidjs.com/).
8
8
 
9
9
  ## 📦 Installation
10
10
  ```sh
11
- bun install @serkonda7/solid-components solid-js
11
+ bun install @serkonda7/solid-components solid-js @tabler/icons-solidjs
12
12
  ```
13
13
 
14
14
 
@@ -26,6 +26,7 @@ import '@serkonda7/solid-components/styles.css'
26
26
 
27
27
  These components are available:
28
28
  - `DataTable` - generic table with optional sorting, row selection, actions, loading, empty states, and column customizer.
29
+ - `Combobox` - searchable, accessible dropdown with keyboard navigation, option icons, a detail column, and an optional "Add …" entry.
29
30
 
30
31
  For the full API, examples, and agent notes, see [docs.md](docs.md).
31
32
 
@@ -0,0 +1,48 @@
1
+ import type { Component, JSX } from 'solid-js';
2
+ /** Icon component rendered before an option label, e.g. one from `@tabler/icons-solidjs`. */
3
+ export type ComboboxIcon = Component<{
4
+ size?: number;
5
+ }>;
6
+ /** One `Combobox` entry. */
7
+ export interface ComboboxOption {
8
+ value: number | string;
9
+ label: string;
10
+ /** Secondary text (e.g. the manufacturer), shown muted in a column after the labels. */
11
+ detail?: string;
12
+ /** Glyph shown before the label, in the list and in the closed input. */
13
+ icon?: ComboboxIcon;
14
+ }
15
+ export interface ComboboxProps {
16
+ /** Input id, for pairing with an external `<label for>`. Generated when omitted. */
17
+ id?: string;
18
+ /** Selected option value as a string; `''` means nothing (or the empty entry) is selected. */
19
+ value: string;
20
+ /** Omitted on read-only comboboxes. */
21
+ onChange?: (value: string) => void;
22
+ options: ComboboxOption[];
23
+ /** Label of the empty (`''`) first entry; omit for no empty choice. */
24
+ emptyLabel?: string;
25
+ required?: boolean;
26
+ disabled?: boolean;
27
+ autofocus?: boolean;
28
+ ariaLabel?: string;
29
+ describedBy?: string;
30
+ /** Called whenever the list opens, e.g. to refetch the options. */
31
+ onOpen?: () => unknown;
32
+ /** Pins an "Add …" entry at the end of the list; receives the trimmed search text. */
33
+ onAdd?: (query: string) => void;
34
+ /** Text of the pinned add entry. Defaults to `Add`. */
35
+ addLabel?: string;
36
+ /** Placeholder while open and nothing is selected. Defaults to `Search`. */
37
+ searchLabel?: string;
38
+ /** Shown when no option matches the search. Defaults to `No matching objects`. */
39
+ noMatchesLabel?: string;
40
+ class?: string;
41
+ }
42
+ /**
43
+ * Searchable dropdown of `options`, with an optional leading empty choice.
44
+ * A text combobox: typing filters the options by label and detail, arrow keys
45
+ * move the highlight (wrapping around), Enter picks it, Escape closes. The
46
+ * input shows the selected label while closed; the value sits in `data-value`.
47
+ */
48
+ export declare function Combobox(props: ComboboxProps): JSX.Element;
package/dist/index.d.ts CHANGED
@@ -4,4 +4,5 @@
4
4
  * Add components here as they are implemented so consumers can import them
5
5
  * from the package root.
6
6
  */
7
+ export { Combobox, type ComboboxIcon, type ComboboxOption, type ComboboxProps, } from './combobox';
7
8
  export { DataTable, type DataTableColumn, type DataTableProps, type DataTableSort, type DataTableSortDirection, } from './data-table';
package/dist/index.js CHANGED
@@ -1,209 +1,389 @@
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, createSignal as p, createUniqueId as m, onCleanup as h } from "solid-js";
1
+ import { Dynamic as e, className as t, createComponent as n, delegateEvents as r, effect as i, insert as a, memo as o, setAttribute as s, template as c, use as l } from "solid-js/web";
2
+ import { IconChevronDown as u, IconPlus as d } from "@tabler/icons-solidjs";
3
+ import { For as f, Show as p, createEffect as m, createMemo as h, createSignal as g, createUniqueId as _, onCleanup as v, onMount as y } from "solid-js";
4
+ //#region src/combobox.tsx
5
+ var b = /*#__PURE__*/ c("<span class=combobox-value aria-hidden=true><span class=combobox-option-label></span><span class=combobox-detail>"), x = /*#__PURE__*/ c("<div role=option class=\"combobox-option combobox-option-add\">"), S = /*#__PURE__*/ c("<div class=combobox-list role=listbox>"), C = /*#__PURE__*/ c("<div><input class=combobox-input role=combobox aria-autocomplete=list autocomplete=off>"), w = /*#__PURE__*/ c("<span class=combobox-icon aria-hidden=true>"), T = /*#__PURE__*/ c("<div class=combobox-empty>"), E = /*#__PURE__*/ c("<span class=combobox-detail>"), D = /*#__PURE__*/ c("<div role=option class=combobox-option><span class=combobox-option-label>"), ee = /*#__PURE__*/ c("<span class=combobox-option-icon aria-hidden=true>");
6
+ function O(r) {
7
+ let c, v, O = _(), k = () => r.id ?? O, A = () => `${k()}-listbox`, j = (e) => `${k()}-option-${e}`, [M, N] = g(!1), [P, F] = g(""), [I, L] = g(0), R = h(() => r.emptyLabel === void 0 ? r.options : [{
8
+ value: "",
9
+ label: r.emptyLabel
10
+ }, ...r.options]), z = h(() => r.value === "" ? void 0 : R().find((e) => String(e.value) === r.value)), B = h(() => {
11
+ let e = P().trim().toLowerCase();
12
+ return e === "" ? R() : R().filter((t) => t.label.toLowerCase().includes(e) || (t.detail?.toLowerCase().includes(e) ?? !1));
13
+ }), V = () => r.options.some((e) => e.detail !== void 0), H = () => B().length, U = () => B().length + +!!r.onAdd, W = () => !r.disabled && r.onChange !== void 0;
14
+ y(() => {
15
+ r.autofocus === !0 && c?.focus();
16
+ }), m(() => {
17
+ M() && v?.querySelector(`[data-index="${I()}"]`)?.scrollIntoView({ block: "nearest" });
18
+ });
19
+ function G() {
20
+ if (M() || !W()) return;
21
+ F("");
22
+ let e = R().findIndex((e) => String(e.value) === r.value);
23
+ L(Math.max(e, 0)), N(!0), r.onOpen?.();
24
+ }
25
+ function K(e) {
26
+ if (N(!1), r.onAdd && e === H()) {
27
+ r.onAdd(P().trim());
28
+ return;
29
+ }
30
+ let t = B()[e];
31
+ t && String(t.value) !== r.value && r.onChange?.(String(t.value));
32
+ }
33
+ function q(e) {
34
+ let t = U();
35
+ M() ? t > 0 && L((I() + e + t) % t) : G();
36
+ }
37
+ function J(e) {
38
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
39
+ e.preventDefault(), q(e.key === "ArrowDown" ? 1 : -1);
40
+ return;
41
+ }
42
+ M() && (e.key === "Enter" ? (e.preventDefault(), K(I())) : e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), N(!1)));
43
+ }
44
+ let te = () => M() ? z()?.label ?? r.emptyLabel ?? r.searchLabel ?? "Search" : r.emptyLabel;
45
+ return (() => {
46
+ var m = C(), h = m.firstChild;
47
+ h.addEventListener("blur", () => N(!1)), h.$$keydown = J, h.$$input = (e) => {
48
+ let t = e.currentTarget.value;
49
+ G(), F(t), L(0);
50
+ }, h.$$click = G;
51
+ var g = c;
52
+ return typeof g == "function" ? l(g, h) : c = h, a(m, n(p, {
53
+ get when() {
54
+ return o(() => !M())() && z()?.icon;
55
+ },
56
+ children: (t) => (() => {
57
+ var r = w();
58
+ return a(r, n(e, {
59
+ get component() {
60
+ return t();
61
+ },
62
+ size: 14
63
+ })), r;
64
+ })()
65
+ }), null), a(m, n(p, {
66
+ get when() {
67
+ return o(() => !M())() && z()?.detail;
68
+ },
69
+ get children() {
70
+ var e = b(), t = e.firstChild, n = t.nextSibling;
71
+ return a(t, () => z()?.label), a(n, () => z()?.detail), e;
72
+ }
73
+ }), null), a(m, n(u, {
74
+ class: "combobox-chevron",
75
+ size: 16,
76
+ "aria-hidden": "true"
77
+ }), null), a(m, n(p, {
78
+ get when() {
79
+ return M();
80
+ },
81
+ get children() {
82
+ var t = S(), o = v;
83
+ return typeof o == "function" ? l(o, t) : v = t, a(t, n(f, {
84
+ get each() {
85
+ return B();
86
+ },
87
+ get fallback() {
88
+ return (() => {
89
+ var e = T();
90
+ return a(e, () => r.noMatchesLabel ?? "No matching objects"), e;
91
+ })();
92
+ },
93
+ children: (t, o) => (() => {
94
+ var c = D(), l = c.firstChild;
95
+ return c.$$click = () => K(o()), c.$$mousemove = () => L(o()), c.$$mousedown = (e) => e.preventDefault(), a(l, n(p, {
96
+ get when() {
97
+ return t.icon;
98
+ },
99
+ children: (t) => (() => {
100
+ var r = ee();
101
+ return a(r, n(e, {
102
+ get component() {
103
+ return t();
104
+ },
105
+ size: 14
106
+ })), r;
107
+ })()
108
+ }), null), a(l, () => t.label, null), a(c, n(p, {
109
+ get when() {
110
+ return t.detail;
111
+ },
112
+ get children() {
113
+ var e = E();
114
+ return a(e, () => t.detail), e;
115
+ }
116
+ }), null), i((e) => {
117
+ var n = j(o()), i = o(), a = String(t.value) === r.value, l = o() === I(), u = t.value === "";
118
+ return n !== e.e && s(c, "id", e.e = n), i !== e.t && s(c, "data-index", e.t = i), a !== e.a && s(c, "aria-selected", e.a = a), l !== e.o && c.classList.toggle("combobox-option-active", e.o = l), u !== e.i && c.classList.toggle("combobox-option-empty", e.i = u), e;
119
+ }, {
120
+ e: void 0,
121
+ t: void 0,
122
+ a: void 0,
123
+ o: void 0,
124
+ i: void 0
125
+ }), c;
126
+ })()
127
+ }), null), a(t, n(p, {
128
+ get when() {
129
+ return r.onAdd;
130
+ },
131
+ get children() {
132
+ var e = x();
133
+ return e.$$click = () => K(H()), e.$$mousemove = () => L(H()), e.$$mousedown = (e) => e.preventDefault(), s(e, "aria-selected", !1), a(e, n(d, {
134
+ size: 14,
135
+ "aria-hidden": "true"
136
+ }), null), a(e, () => r.addLabel ?? "Add", null), i((t) => {
137
+ var n = j(H()), r = H(), i = H() === I();
138
+ return n !== t.e && s(e, "id", t.e = n), r !== t.t && s(e, "data-index", t.t = r), i !== t.a && e.classList.toggle("combobox-option-active", t.a = i), t;
139
+ }, {
140
+ e: void 0,
141
+ t: void 0,
142
+ a: void 0
143
+ }), e;
144
+ }
145
+ }), null), i((e) => {
146
+ var n = !!V(), r = A();
147
+ return n !== e.e && t.classList.toggle("combobox-list-detail", e.e = n), r !== e.t && s(t, "id", e.t = r), e;
148
+ }, {
149
+ e: void 0,
150
+ t: void 0
151
+ }), t;
152
+ }
153
+ }), null), i((e) => {
154
+ var n = r.class ? `combobox ${r.class}` : "combobox", i = !!M(), a = !(M() || !z()?.detail), o = !(M() || z()?.icon === void 0), c = k(), l = M(), u = A(), d = M() && U() > 0 ? j(I()) : void 0, f = r.ariaLabel, p = r.describedBy, g = r.required, _ = r.disabled, v = r.onChange === void 0, y = r.value, b = te();
155
+ return n !== e.e && t(m, e.e = n), i !== e.t && m.classList.toggle("combobox-open", e.t = i), a !== e.a && m.classList.toggle("combobox-with-detail", e.a = a), o !== e.o && m.classList.toggle("combobox-with-icon", e.o = o), c !== e.i && s(h, "id", e.i = c), l !== e.n && s(h, "aria-expanded", e.n = l), u !== e.s && s(h, "aria-controls", e.s = u), d !== e.h && s(h, "aria-activedescendant", e.h = d), f !== e.r && s(h, "aria-label", e.r = f), p !== e.d && s(h, "aria-describedby", e.d = p), g !== e.l && (h.required = e.l = g), _ !== e.u && (h.disabled = e.u = _), v !== e.c && (h.readOnly = e.c = v), y !== e.w && s(h, "data-value", e.w = y), b !== e.m && s(h, "placeholder", e.m = b), e;
156
+ }, {
157
+ e: void 0,
158
+ t: void 0,
159
+ a: void 0,
160
+ o: void 0,
161
+ i: void 0,
162
+ n: void 0,
163
+ s: void 0,
164
+ h: void 0,
165
+ r: void 0,
166
+ d: void 0,
167
+ l: void 0,
168
+ u: void 0,
169
+ c: void 0,
170
+ w: void 0,
171
+ m: void 0
172
+ }), i(() => h.value = M() ? P() : z()?.label ?? ""), m;
173
+ })();
174
+ }
175
+ r([
176
+ "click",
177
+ "input",
178
+ "keydown",
179
+ "mousedown",
180
+ "mousemove"
181
+ ]);
182
+ //#endregion
3
183
  //#region src/data-table.tsx
4
- var ee = /*#__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>"), te = /*#__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> "), ne = /*#__PURE__*/ s("<th class=data-table-checkbox><span class=data-table-visually-hidden>Select rows</span><input type=checkbox>"), g = /*#__PURE__*/ s("<th>Actions"), _ = /*#__PURE__*/ s("<div><div class=data-table-scroll><table class=data-table><thead><tr></tr></thead><tbody>"), v = /*#__PURE__*/ s("<li><label class=data-table-customizer-option><input type=checkbox>"), y = /*#__PURE__*/ s("<button type=button class=data-table-sort><span class=data-table-sort-indicator aria-hidden=true>"), b = /*#__PURE__*/ s("<th>"), re = /*#__PURE__*/ s("<td class=data-table-checkbox><input type=checkbox>"), x = /*#__PURE__*/ s("<td>"), ie = /*#__PURE__*/ s("<tr>"), ae = /*#__PURE__*/ s("<p class=data-table-status>Loading…"), S = /*#__PURE__*/ s("<p class=data-table-status>No results.");
5
- function C(e) {
184
+ var k = /*#__PURE__*/ c("<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>"), A = /*#__PURE__*/ c("<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> "), j = /*#__PURE__*/ c("<th class=data-table-checkbox><span class=data-table-visually-hidden>Select rows</span><input type=checkbox>"), M = /*#__PURE__*/ c("<th>Actions"), N = /*#__PURE__*/ c("<div><div class=data-table-scroll><table class=data-table><thead><tr></tr></thead><tbody>"), P = /*#__PURE__*/ c("<li><label class=data-table-customizer-option><input type=checkbox>"), F = /*#__PURE__*/ c("<button type=button class=data-table-sort><span class=data-table-sort-indicator aria-hidden=true>"), I = /*#__PURE__*/ c("<th>"), L = /*#__PURE__*/ c("<td class=data-table-checkbox><input type=checkbox>"), R = /*#__PURE__*/ c("<td>"), z = /*#__PURE__*/ c("<tr>"), B = /*#__PURE__*/ c("<p class=data-table-status>Loading…"), V = /*#__PURE__*/ c("<p class=data-table-status>No results.");
185
+ function H(e) {
6
186
  return typeof e == "function" ? e() : e;
7
187
  }
8
- function w(n) {
9
- let s = f(() => C(n.rows)), w = () => C(n.loading ?? !1), T = () => C(n.selected ?? []), E = n.sortKey !== void 0 || n.sortDirection !== void 0, [D, O] = p(n.defaultSort), k = () => E ? C(n.sortKey ?? void 0) : D()?.key, A = () => E ? C(n.sortDirection ?? "asc") : D()?.direction ?? "asc", j = () => C(n.showColumnCustomizer ?? !1), M = () => n.selected !== void 0 && n.onSelectionChange !== void 0, N = () => {
10
- let e = T();
11
- return s().length > 0 && s().every((t) => e.includes(n.getRowId(t)));
12
- }, P = f(() => n.defaultVisibleColumns ?? n.columns.filter((e) => e.defaultVisible !== !1).map((e) => e.key)), [F, I] = p(void 0), L = () => n.visibleColumns === void 0 ? F() ?? P() : C(n.visibleColumns), R = f(() => new Set(L())), z = f(() => n.columns.filter((e) => e.toggleable === !1 || R().has(e.key))), B = f(() => n.columns.filter((e) => e.toggleable !== !1)), V = f(() => B().map((e) => e.key));
13
- function H(e, t) {
188
+ function U(e) {
189
+ let r = h(() => H(e.rows)), c = () => H(e.loading ?? !1), u = () => H(e.selected ?? []), d = e.sortKey !== void 0 || e.sortDirection !== void 0, [y, b] = g(e.defaultSort), x = () => d ? H(e.sortKey ?? void 0) : y()?.key, S = () => d ? H(e.sortDirection ?? "asc") : y()?.direction ?? "asc", C = () => H(e.showColumnCustomizer ?? !1), w = () => e.selected !== void 0 && e.onSelectionChange !== void 0, T = () => {
190
+ let t = u();
191
+ return r().length > 0 && r().every((n) => t.includes(e.getRowId(n)));
192
+ }, E = h(() => e.defaultVisibleColumns ?? e.columns.filter((e) => e.defaultVisible !== !1).map((e) => e.key)), [D, ee] = g(void 0), O = () => e.visibleColumns === void 0 ? D() ?? E() : H(e.visibleColumns), U = h(() => new Set(O())), W = h(() => e.columns.filter((e) => e.toggleable === !1 || U().has(e.key))), G = h(() => e.columns.filter((e) => e.toggleable !== !1)), K = h(() => G().map((e) => e.key));
193
+ function q(e, t) {
14
194
  return t.sortValue ? t.sortValue(e) : e[t.key];
15
195
  }
16
- function U(e, t) {
196
+ function J(e, t) {
17
197
  return e === t ? 0 : e == null ? -1 : t == null ? 1 : typeof e == "number" && typeof t == "number" ? e - t : String(e).localeCompare(String(t), void 0, { numeric: !0 });
18
198
  }
19
- let W = f(() => {
20
- let e = s(), t = k();
21
- if (E || !t) return e;
22
- let r = n.columns.find((e) => e.key === t);
23
- if (!r) return e;
24
- let i = A() === "asc" ? 1 : -1;
25
- return [...e].sort((e, t) => i * U(H(e, r), H(t, r)));
199
+ let te = h(() => {
200
+ let t = r(), n = x();
201
+ if (d || !n) return t;
202
+ let i = e.columns.find((e) => e.key === n);
203
+ if (!i) return t;
204
+ let a = S() === "asc" ? 1 : -1;
205
+ return [...t].sort((e, t) => a * J(q(e, i), q(t, i)));
26
206
  });
27
- function G(e) {
28
- let t = k() === e, r = t && A() === "desc" ? void 0 : {
29
- key: e,
30
- direction: t ? "desc" : "asc"
207
+ function ne(t) {
208
+ let n = x() === t, r = n && S() === "desc" ? void 0 : {
209
+ key: t,
210
+ direction: n ? "desc" : "asc"
31
211
  };
32
- E || O(r), n.onSort?.(e), r || n.onSortClear?.(), n.onSortChange?.(r);
212
+ d || b(r), e.onSort?.(t), r || e.onSortClear?.(), e.onSortChange?.(r);
33
213
  }
34
- function K(e) {
35
- let t = new Map(n.columns.map((e, t) => [e.key, t]));
36
- return [...e].sort((e, n) => (t.get(e) ?? 0) - (t.get(n) ?? 0));
214
+ function re(t) {
215
+ let n = new Map(e.columns.map((e, t) => [e.key, t]));
216
+ return [...t].sort((e, t) => (n.get(e) ?? 0) - (n.get(t) ?? 0));
37
217
  }
38
- function q(e) {
39
- let t = K(e);
40
- n.visibleColumns === void 0 && I(t), n.onVisibleColumnsChange?.(t);
218
+ function Y(t) {
219
+ let n = re(t);
220
+ e.visibleColumns === void 0 && ee(n), e.onVisibleColumnsChange?.(n);
41
221
  }
42
- function oe(e, t) {
43
- let n = new Set(L());
44
- t ? n.add(e) : n.delete(e), q(V().filter((e) => n.has(e)));
222
+ function ie(e, t) {
223
+ let n = new Set(O());
224
+ t ? n.add(e) : n.delete(e), Y(K().filter((e) => n.has(e)));
45
225
  }
46
- function se() {
47
- q(V());
226
+ function ae() {
227
+ Y(K());
48
228
  }
49
- function ce() {
50
- q(P());
229
+ function oe() {
230
+ Y(E());
51
231
  }
52
- let J = `${m()}-panel`, [Y, X] = p(!1), Z;
53
- d(() => {
54
- if (!Y() || !j()) return;
232
+ let se = `${_()}-panel`, [X, Z] = g(!1), Q;
233
+ m(() => {
234
+ if (!X() || !C()) return;
55
235
  function e(e) {
56
- Z && !Z.contains(e.target) && X(!1);
236
+ Q && !Q.contains(e.target) && Z(!1);
57
237
  }
58
238
  function t(e) {
59
- e.key === "Escape" && X(!1);
239
+ e.key === "Escape" && Z(!1);
60
240
  }
61
- document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), h(() => {
241
+ document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), v(() => {
62
242
  document.removeEventListener("pointerdown", e), document.removeEventListener("keydown", t);
63
243
  });
64
244
  });
65
- let Q;
66
- d(() => {
67
- Q && (Q.indeterminate = T().length > 0 && !N());
245
+ let $;
246
+ m(() => {
247
+ $ && ($.indeterminate = u().length > 0 && !T());
68
248
  });
69
- function $(e) {
70
- n.onSelectionChange?.(e ? s().map(n.getRowId) : []);
249
+ function ce(t) {
250
+ e.onSelectionChange?.(t ? r().map(e.getRowId) : []);
71
251
  }
72
- function le(e, t) {
73
- let r = n.getRowId(e), i = T(), a = t ? [...i, r] : i.filter((e) => e !== r);
74
- n.onSelectionChange?.(a);
252
+ function le(t, n) {
253
+ let r = e.getRowId(t), i = u(), a = n ? [...i, r] : i.filter((e) => e !== r);
254
+ e.onSelectionChange?.(a);
75
255
  }
76
- function ue(e) {
77
- return T().includes(n.getRowId(e));
256
+ function ue(t) {
257
+ return u().includes(e.getRowId(t));
78
258
  }
79
259
  function de(e) {
80
- return e.toggleable === !1 || R().has(e.key);
260
+ return e.toggleable === !1 || U().has(e.key);
81
261
  }
82
- function fe(e, t) {
83
- return n.getCell?.(e, t) ?? t.getValue?.(e) ?? null;
262
+ function fe(t, n) {
263
+ return e.getCell?.(t, n) ?? n.getValue?.(t) ?? null;
84
264
  }
85
265
  return (() => {
86
- var d = _(), f = d.firstChild, p = f.firstChild.firstChild, m = p.firstChild, h = p.nextSibling;
87
- return i(d, t(u, {
266
+ var u = N(), m = u.firstChild, h = m.firstChild.firstChild, g = h.firstChild, _ = h.nextSibling;
267
+ return a(u, n(p, {
88
268
  get when() {
89
- return j();
269
+ return C();
90
270
  },
91
271
  get children() {
92
- var e = te(), a = e.firstChild, s = a.firstChild;
93
- s.firstChild.nextSibling;
94
- var d = Z;
95
- return typeof d == "function" ? c(d, a) : Z = a, s.$$click = () => X((e) => !e), o(s, "aria-controls", J), i(s, () => n.columnCustomizerLabel ?? "Columns", null), i(a, t(u, {
272
+ var t = A(), r = t.firstChild, o = r.firstChild;
273
+ o.firstChild.nextSibling;
274
+ var c = Q;
275
+ return typeof c == "function" ? l(c, r) : Q = r, o.$$click = () => Z((e) => !e), s(o, "aria-controls", se), a(o, () => e.columnCustomizerLabel ?? "Columns", null), a(r, n(p, {
96
276
  get when() {
97
- return Y();
277
+ return X();
98
278
  },
99
279
  get children() {
100
- var e = ee(), a = e.firstChild, s = a.nextSibling, c = s.nextSibling.firstChild, u = c.nextSibling;
101
- return o(e, "id", J), i(a, () => n.columnCustomizerTitle ?? "Shown columns"), i(s, t(l, {
280
+ var t = k(), r = t.firstChild, o = r.nextSibling, c = o.nextSibling.firstChild, l = c.nextSibling;
281
+ return s(t, "id", se), a(r, () => e.columnCustomizerTitle ?? "Shown columns"), a(o, n(f, {
102
282
  get each() {
103
- return n.columns;
283
+ return e.columns;
104
284
  },
105
285
  children: (e) => (() => {
106
- var t = v(), n = t.firstChild, a = n.firstChild;
107
- return a.addEventListener("change", (t) => oe(e.key, t.currentTarget.checked)), i(n, () => e.ariaLabel ?? e.label, null), r(() => a.disabled = e.toggleable === !1), r(() => a.checked = de(e)), t;
286
+ var t = P(), n = t.firstChild, r = n.firstChild;
287
+ return r.addEventListener("change", (t) => ie(e.key, t.currentTarget.checked)), a(n, () => e.ariaLabel ?? e.label, null), i(() => r.disabled = e.toggleable === !1), i(() => r.checked = de(e)), t;
108
288
  })()
109
- })), c.$$click = se, i(c, () => n.columnCustomizerShowAllLabel ?? "Show all"), u.$$click = ce, i(u, () => n.columnCustomizerResetLabel ?? "Reset"), r(() => o(e, "aria-label", n.columnCustomizerTitle ?? "Customize columns")), e;
289
+ })), c.$$click = ae, a(c, () => e.columnCustomizerShowAllLabel ?? "Show all"), l.$$click = oe, a(l, () => e.columnCustomizerResetLabel ?? "Reset"), i(() => s(t, "aria-label", e.columnCustomizerTitle ?? "Customize columns")), t;
110
290
  }
111
- }), null), r(() => o(s, "aria-expanded", Y())), e;
291
+ }), null), i(() => s(o, "aria-expanded", X())), t;
112
292
  }
113
- }), f), i(m, t(u, {
293
+ }), m), a(g, n(p, {
114
294
  get when() {
115
- return M();
295
+ return w();
116
296
  },
117
297
  get children() {
118
- var e = ne(), t = e.firstChild.nextSibling;
119
- t.addEventListener("change", (e) => $(e.currentTarget.checked));
120
- var i = Q;
121
- return typeof i == "function" ? c(i, t) : Q = t, r(() => o(t, "aria-label", n.selectionLabel ?? "Select all rows")), r(() => t.checked = N()), e;
298
+ var t = j(), n = t.firstChild.nextSibling;
299
+ n.addEventListener("change", (e) => ce(e.currentTarget.checked));
300
+ var r = $;
301
+ return typeof r == "function" ? l(r, n) : $ = n, i(() => s(n, "aria-label", e.selectionLabel ?? "Select all rows")), i(() => n.checked = T()), t;
122
302
  }
123
- }), null), i(m, t(l, {
303
+ }), null), a(g, n(f, {
124
304
  get each() {
125
- return z();
305
+ return W();
126
306
  },
127
- children: (s) => (() => {
128
- var c = b();
129
- return i(c, t(u, {
307
+ children: (r) => (() => {
308
+ var c = I();
309
+ return a(c, n(p, {
130
310
  get when() {
131
- return a(() => !!s.sortable)() && (!E || n.onSort || n.onSortChange);
311
+ return o(() => !!r.sortable)() && (!d || e.onSort || e.onSortChange);
132
312
  },
133
313
  get fallback() {
134
- return s.label;
314
+ return r.label;
135
315
  },
136
316
  get children() {
137
- var e = y(), t = e.firstChild;
138
- return e.$$click = () => G(s.key), i(e, () => s.label, t), i(t, (() => {
139
- var e = a(() => k() === s.key);
140
- return () => e() ? A() === "asc" ? "▲" : "▼" : "";
317
+ var e = F(), t = e.firstChild;
318
+ return e.$$click = () => ne(r.key), a(e, () => r.label, t), a(t, (() => {
319
+ var e = o(() => x() === r.key);
320
+ return () => e() ? S() === "asc" ? "▲" : "▼" : "";
141
321
  })()), e;
142
322
  }
143
- })), r((t) => {
144
- var n = s.class, r = s.sortable ? k() === s.key ? A() === "asc" ? "ascending" : "descending" : "none" : void 0;
145
- return n !== t.e && e(c, t.e = n), r !== t.t && o(c, "aria-sort", t.t = r), t;
323
+ })), i((e) => {
324
+ var n = r.class, i = r.sortable ? x() === r.key ? S() === "asc" ? "ascending" : "descending" : "none" : void 0;
325
+ return n !== e.e && t(c, e.e = n), i !== e.t && s(c, "aria-sort", e.t = i), e;
146
326
  }, {
147
327
  e: void 0,
148
328
  t: void 0
149
329
  }), c;
150
330
  })()
151
- }), null), i(m, t(u, {
331
+ }), null), a(g, n(p, {
152
332
  get when() {
153
- return n.rowActions;
333
+ return e.rowActions;
154
334
  },
155
335
  get children() {
156
- return g();
336
+ return M();
157
337
  }
158
- }), null), i(h, t(l, {
338
+ }), null), a(_, n(f, {
159
339
  get each() {
160
- return W();
340
+ return te();
161
341
  },
162
- children: (a) => (() => {
163
- var s = ie();
164
- return i(s, t(u, {
342
+ children: (r) => (() => {
343
+ var o = z();
344
+ return a(o, n(p, {
165
345
  get when() {
166
- return M();
346
+ return w();
167
347
  },
168
348
  get children() {
169
- var e = re(), t = e.firstChild;
170
- return t.addEventListener("change", (e) => le(a, e.currentTarget.checked)), r(() => o(t, "aria-label", `Select row ${n.getRowId(a)}`)), r(() => t.checked = ue(a)), e;
349
+ var t = L(), n = t.firstChild;
350
+ return n.addEventListener("change", (e) => le(r, e.currentTarget.checked)), i(() => s(n, "aria-label", `Select row ${e.getRowId(r)}`)), i(() => n.checked = ue(r)), t;
171
351
  }
172
- }), null), i(s, t(l, {
352
+ }), null), a(o, n(f, {
173
353
  get each() {
174
- return z();
354
+ return W();
175
355
  },
176
- children: (t) => (() => {
177
- var n = x();
178
- return i(n, () => fe(a, t)), r(() => e(n, t.class)), n;
356
+ children: (e) => (() => {
357
+ var n = R();
358
+ return a(n, () => fe(r, e)), i(() => t(n, e.class)), n;
179
359
  })()
180
- }), null), i(s, t(u, {
360
+ }), null), a(o, n(p, {
181
361
  get when() {
182
- return n.rowActions;
362
+ return e.rowActions;
183
363
  },
184
364
  get children() {
185
- var e = x();
186
- return i(e, () => n.rowActions?.(a)), e;
365
+ var t = R();
366
+ return a(t, () => e.rowActions?.(r)), t;
187
367
  }
188
- }), null), s;
368
+ }), null), o;
189
369
  })()
190
- })), i(d, t(u, {
370
+ })), a(u, n(p, {
191
371
  get when() {
192
- return w();
372
+ return c();
193
373
  },
194
374
  get children() {
195
- return n.loadingContent ?? ae();
375
+ return e.loadingContent ?? B();
196
376
  }
197
- }), null), i(d, t(u, {
377
+ }), null), a(u, n(p, {
198
378
  get when() {
199
- return a(() => !w())() && (n.empty === void 0 ? s().length === 0 : C(n.empty));
379
+ return o(() => !c())() && (e.empty === void 0 ? r().length === 0 : H(e.empty));
200
380
  },
201
381
  get children() {
202
- return n.emptyContent ?? S();
382
+ return e.emptyContent ?? V();
203
383
  }
204
- }), null), r(() => e(d, `data-table-wrap${n.class ? ` ${n.class}` : ""}`)), d;
384
+ }), null), i(() => t(u, `data-table-wrap${e.class ? ` ${e.class}` : ""}`)), u;
205
385
  })();
206
386
  }
207
- n(["click"]);
387
+ r(["click"]);
208
388
  //#endregion
209
- export { w as DataTable };
389
+ export { O as Combobox, U as DataTable };
package/dist/styles.css CHANGED
@@ -137,3 +137,221 @@
137
137
  clip: rect(0 0 0 0);
138
138
  white-space: nowrap;
139
139
  }
140
+
141
+ /* Combobox: text input plus an absolutely positioned listbox below it. */
142
+ .combobox {
143
+ position: relative;
144
+ min-width: 0;
145
+ }
146
+
147
+ .combobox-input {
148
+ box-sizing: border-box;
149
+ width: 100%;
150
+ padding: 0.375rem 1.8rem 0.375rem 0.5rem;
151
+ border: 1px solid
152
+ var(--combobox-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
153
+ border-radius: 0.375rem;
154
+ background: var(
155
+ --combobox-bg,
156
+ var(--surface, light-dark(#ffffff, #1d2024))
157
+ );
158
+ color: inherit;
159
+ font: inherit;
160
+ text-overflow: ellipsis;
161
+ cursor: pointer;
162
+ }
163
+
164
+ .combobox-open .combobox-input {
165
+ cursor: text;
166
+ }
167
+
168
+ .combobox-input:disabled {
169
+ cursor: not-allowed;
170
+ opacity: 0.6;
171
+ }
172
+
173
+ .combobox-with-detail .combobox-input {
174
+ /* The `.combobox-value` overlay renders the label alongside its detail. */
175
+ color: transparent;
176
+ }
177
+
178
+ .combobox-with-icon .combobox-input {
179
+ padding-left: 1.7rem;
180
+ }
181
+
182
+ /* Closed-state glyph of the selected option, inside the input's left padding. */
183
+ .combobox-icon {
184
+ position: absolute;
185
+ top: 50%;
186
+ left: 0.55rem;
187
+ display: inline-flex;
188
+ transform: translateY(-50%);
189
+ color: var(
190
+ --combobox-muted,
191
+ var(--text-secondary, light-dark(#616161, #9b9da5))
192
+ );
193
+ pointer-events: none;
194
+ }
195
+
196
+ .combobox-option-icon {
197
+ display: inline-flex;
198
+ margin-right: 0.4rem;
199
+ vertical-align: -0.15em;
200
+ color: var(
201
+ --combobox-muted,
202
+ var(--text-secondary, light-dark(#616161, #9b9da5))
203
+ );
204
+ }
205
+
206
+ .combobox-chevron {
207
+ position: absolute;
208
+ top: 50%;
209
+ right: 0.5rem;
210
+ transform: translateY(-50%);
211
+ color: var(
212
+ --combobox-muted,
213
+ var(--text-secondary, light-dark(#616161, #9b9da5))
214
+ );
215
+ pointer-events: none;
216
+ }
217
+
218
+ .combobox-list {
219
+ position: absolute;
220
+ top: calc(100% + 0.2rem);
221
+ left: 0;
222
+ right: 0;
223
+ z-index: 20;
224
+ max-height: 16rem;
225
+ overflow-y: auto;
226
+ margin: 0;
227
+ padding: 0.2rem;
228
+ border: 1px solid
229
+ var(--combobox-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
230
+ border-radius: 0.375rem;
231
+ background: var(
232
+ --combobox-panel-bg,
233
+ var(--surface, light-dark(#ffffff, #1d2024))
234
+ );
235
+ box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
236
+ }
237
+
238
+ .combobox-option,
239
+ .combobox-empty {
240
+ padding: 0.3rem 0.5rem;
241
+ border-radius: 0.25rem;
242
+ }
243
+
244
+ .combobox-option {
245
+ cursor: pointer;
246
+ }
247
+
248
+ .combobox-option-label,
249
+ .combobox-detail {
250
+ overflow: hidden;
251
+ text-overflow: ellipsis;
252
+ white-space: nowrap;
253
+ }
254
+
255
+ .combobox-detail {
256
+ color: var(
257
+ --combobox-muted,
258
+ var(--text-secondary, light-dark(#616161, #9b9da5))
259
+ );
260
+ font-weight: 400;
261
+ font-size: 0.875em;
262
+ }
263
+
264
+ /* Closed-state value with detail, overlaid on the (transparent) input text. */
265
+ .combobox-value {
266
+ position: absolute;
267
+ top: 50%;
268
+ left: calc(0.5rem + 1px);
269
+ right: 1.8rem;
270
+ transform: translateY(-50%);
271
+ display: flex;
272
+ align-items: baseline;
273
+ gap: 0.6rem;
274
+ min-width: 0;
275
+ pointer-events: none;
276
+ }
277
+
278
+ .combobox-with-icon .combobox-value {
279
+ left: calc(1.7rem + 1px);
280
+ }
281
+
282
+ .combobox-value > .combobox-option-label {
283
+ flex: 0 1 auto;
284
+ min-width: 3rem;
285
+ }
286
+
287
+ .combobox-value > .combobox-detail {
288
+ flex: 0 1 auto;
289
+ min-width: 0;
290
+ }
291
+
292
+ .combobox-option-active {
293
+ background: var(
294
+ --combobox-hover-bg,
295
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
296
+ );
297
+ }
298
+
299
+ .combobox-option[aria-selected="true"] {
300
+ font-weight: 600;
301
+ }
302
+
303
+ /* Pinned "Add …" entry below the options, kept in view while scrolling. */
304
+ .combobox-option-add {
305
+ position: sticky;
306
+ bottom: -0.2rem;
307
+ display: flex;
308
+ align-items: center;
309
+ gap: 0.4rem;
310
+ margin-top: 0.2rem;
311
+ border-top: 1px solid
312
+ var(--combobox-border, var(--border, light-dark(#d0d0d0, #3a3e45)));
313
+ border-radius: 0 0 0.25rem 0.25rem;
314
+ background: var(
315
+ --combobox-panel-bg,
316
+ var(--surface, light-dark(#ffffff, #1d2024))
317
+ );
318
+ color: var(--combobox-accent, var(--primary, light-dark(#1a66d6, #6ea8fe)));
319
+ font-weight: 600;
320
+ }
321
+
322
+ .combobox-option-add.combobox-option-active {
323
+ background: var(
324
+ --combobox-hover-bg,
325
+ var(--surface-hover, light-dark(#f5f5f5, #292d33))
326
+ );
327
+ }
328
+
329
+ .combobox-option-empty,
330
+ .combobox-empty {
331
+ color: var(
332
+ --combobox-muted,
333
+ var(--text-secondary, light-dark(#616161, #9b9da5))
334
+ );
335
+ }
336
+
337
+ /* Options with a detail line up in two columns: the detail starts right
338
+ after the longest label. */
339
+ .combobox-list-detail {
340
+ display: grid;
341
+ grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
342
+ column-gap: 1rem;
343
+ align-content: start;
344
+ }
345
+
346
+ .combobox-list-detail > .combobox-option,
347
+ .combobox-list-detail > .combobox-empty {
348
+ display: grid;
349
+ grid-column: 1 / -1;
350
+ grid-template-columns: subgrid;
351
+ align-items: baseline;
352
+ }
353
+
354
+ .combobox-list-detail > .combobox-option-add {
355
+ display: flex;
356
+ align-items: center;
357
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serkonda7/solid-components",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Reusable components for SolidJS",
5
5
  "license": "MIT",
6
6
  "author": "serkonda7",
@@ -39,17 +39,19 @@
39
39
  },
40
40
  "packageManager": "bun@1.4.2",
41
41
  "peerDependencies": {
42
+ "@tabler/icons-solidjs": ">=3.0.0",
42
43
  "solid-js": ">=1.9.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@biomejs/biome": "2.5.14",
47
+ "@tabler/icons-solidjs": "~3.48.0",
46
48
  "@types/bun": "1.4.2",
47
49
  "@vitest/browser-playwright": "^5.0.2",
48
- "playwright": "^1.63.0",
50
+ "playwright": "~1.63.0",
49
51
  "solid-js": "~1.9.15",
50
52
  "typescript": "~7.0.2",
51
53
  "vite": "~8.3.0",
52
54
  "vite-plugin-solid": "~2.11.14",
53
- "vitest": "^5.0.2"
55
+ "vitest": "~5.0.2"
54
56
  }
55
57
  }