@serkonda7/solid-components 0.0.6 → 0.0.8

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
@@ -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,444 @@
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, mergeProps as s, setAttribute as c, spread as l, template as u, use as d } from "solid-js/web";
2
+ import { For as f, Show as p, createEffect as m, createMemo as h, createSignal as g, createUniqueId as _, onCleanup as v, onMount as y, splitProps as b } from "solid-js";
3
+ //#region node_modules/@tabler/icons-solidjs/dist/source/defaultAttributes.js
4
+ var x = {
5
+ outline: {
6
+ xmlns: "http://www.w3.org/2000/svg",
7
+ width: 24,
8
+ height: 24,
9
+ viewBox: "0 0 24 24",
10
+ fill: "none",
11
+ stroke: "currentColor",
12
+ "stroke-width": 2,
13
+ "stroke-linecap": "round",
14
+ "stroke-linejoin": "round"
15
+ },
16
+ filled: {
17
+ xmlns: "http://www.w3.org/2000/svg",
18
+ width: 24,
19
+ height: 24,
20
+ viewBox: "0 0 24 24",
21
+ fill: "currentColor",
22
+ stroke: "none"
23
+ }
24
+ }, S = /*#__PURE__*/ u("<svg>"), C = /*#__PURE__*/ u("<title>"), w = (t, r, i, c) => {
25
+ let u = (i) => {
26
+ let [u, d] = b(i, [
27
+ "color",
28
+ "size",
29
+ "stroke",
30
+ "strokeWidth",
31
+ "title",
32
+ "children",
33
+ "class"
34
+ ]), f = x[t], p = u.stroke ?? u.strokeWidth;
35
+ return (() => {
36
+ var i = S();
37
+ return l(i, s(f, d, {
38
+ get width() {
39
+ return o(() => u.size != null)() ? u.size : f.width;
40
+ },
41
+ get height() {
42
+ return o(() => u.size != null)() ? u.size : f.height;
43
+ },
44
+ get class() {
45
+ return `tabler-icon tabler-icon-${r}${u.class ? ` ${u.class}` : ""}`;
46
+ }
47
+ }, () => t === "filled" ? { fill: u.color == null ? "currentColor" : u.color } : {
48
+ stroke: u.color == null ? "currentColor" : u.color,
49
+ "stroke-width": p ?? f["stroke-width"]
50
+ }), !0, !0), a(i, (() => {
51
+ var e = o(() => u.title != null);
52
+ return () => e() && (() => {
53
+ var e = C();
54
+ return a(e, () => u.title), e;
55
+ })();
56
+ })(), null), a(i, () => c.map(([t, r]) => n(e, s({ component: t }, r))), null), a(i, () => u.children, null), i;
57
+ })();
58
+ };
59
+ return u.displayName = `${i}`, u;
60
+ }, T = w("outline", "chevron-down", "ChevronDown", [["path", { d: "M6 9l6 6l6 -6" }]]), E = w("outline", "plus", "Plus", [["path", { d: "M12 5l0 14" }], ["path", { d: "M5 12l14 0" }]]), D = /*#__PURE__*/ u("<span class=combobox-value aria-hidden=true><span class=combobox-option-label></span><span class=combobox-detail>"), ee = /*#__PURE__*/ u("<div role=option class=\"combobox-option combobox-option-add\">"), O = /*#__PURE__*/ u("<div class=combobox-list role=listbox>"), k = /*#__PURE__*/ u("<div><input class=combobox-input role=combobox aria-autocomplete=list autocomplete=off>"), A = /*#__PURE__*/ u("<span class=combobox-icon aria-hidden=true>"), te = /*#__PURE__*/ u("<div class=combobox-empty>"), j = /*#__PURE__*/ u("<span class=combobox-detail>"), M = /*#__PURE__*/ u("<div role=option class=combobox-option><span class=combobox-option-label>"), N = /*#__PURE__*/ u("<span class=combobox-option-icon aria-hidden=true>");
61
+ function P(r) {
62
+ let s, l, u = _(), v = () => r.id ?? u, b = () => `${v()}-listbox`, x = (e) => `${v()}-option-${e}`, [S, C] = g(!1), [w, P] = g(""), [F, I] = g(0), L = h(() => r.emptyLabel === void 0 ? r.options : [{
63
+ value: "",
64
+ label: r.emptyLabel
65
+ }, ...r.options]), R = h(() => r.value === "" ? void 0 : L().find((e) => String(e.value) === r.value)), z = h(() => {
66
+ let e = w().trim().toLowerCase();
67
+ return e === "" ? L() : L().filter((t) => t.label.toLowerCase().includes(e) || (t.detail?.toLowerCase().includes(e) ?? !1));
68
+ }), ne = () => r.options.some((e) => e.detail !== void 0), B = () => z().length, V = () => z().length + +!!r.onAdd, re = () => !r.disabled && r.onChange !== void 0;
69
+ y(() => {
70
+ r.autofocus === !0 && s?.focus();
71
+ }), m(() => {
72
+ S() && l?.querySelector(`[data-index="${F()}"]`)?.scrollIntoView({ block: "nearest" });
73
+ });
74
+ function H() {
75
+ if (S() || !re()) return;
76
+ P("");
77
+ let e = L().findIndex((e) => String(e.value) === r.value);
78
+ I(Math.max(e, 0)), C(!0), r.onOpen?.();
79
+ }
80
+ function U(e) {
81
+ if (C(!1), r.onAdd && e === B()) {
82
+ r.onAdd(w().trim());
83
+ return;
84
+ }
85
+ let t = z()[e];
86
+ t && String(t.value) !== r.value && r.onChange?.(String(t.value));
87
+ }
88
+ function W(e) {
89
+ let t = V();
90
+ S() ? t > 0 && I((F() + e + t) % t) : H();
91
+ }
92
+ function G(e) {
93
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
94
+ e.preventDefault(), W(e.key === "ArrowDown" ? 1 : -1);
95
+ return;
96
+ }
97
+ S() && (e.key === "Enter" ? (e.preventDefault(), U(F())) : e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), C(!1)));
98
+ }
99
+ let K = () => S() ? R()?.label ?? r.emptyLabel ?? r.searchLabel ?? "Search" : r.emptyLabel;
100
+ return (() => {
101
+ var u = k(), m = u.firstChild;
102
+ m.addEventListener("blur", () => C(!1)), m.$$keydown = G, m.$$input = (e) => {
103
+ let t = e.currentTarget.value;
104
+ H(), P(t), I(0);
105
+ }, m.$$click = H;
106
+ var h = s;
107
+ return typeof h == "function" ? d(h, m) : s = m, a(u, n(p, {
108
+ get when() {
109
+ return o(() => !S())() && R()?.icon;
110
+ },
111
+ children: (t) => (() => {
112
+ var r = A();
113
+ return a(r, n(e, {
114
+ get component() {
115
+ return t();
116
+ },
117
+ size: 14
118
+ })), r;
119
+ })()
120
+ }), null), a(u, n(p, {
121
+ get when() {
122
+ return o(() => !S())() && R()?.detail;
123
+ },
124
+ get children() {
125
+ var e = D(), t = e.firstChild, n = t.nextSibling;
126
+ return a(t, () => R()?.label), a(n, () => R()?.detail), e;
127
+ }
128
+ }), null), a(u, n(T, {
129
+ class: "combobox-chevron",
130
+ size: 16,
131
+ "aria-hidden": "true"
132
+ }), null), a(u, n(p, {
133
+ get when() {
134
+ return S();
135
+ },
136
+ get children() {
137
+ var t = O(), o = l;
138
+ return typeof o == "function" ? d(o, t) : l = t, a(t, n(f, {
139
+ get each() {
140
+ return z();
141
+ },
142
+ get fallback() {
143
+ return (() => {
144
+ var e = te();
145
+ return a(e, () => r.noMatchesLabel ?? "No matching objects"), e;
146
+ })();
147
+ },
148
+ children: (t, o) => (() => {
149
+ var s = M(), l = s.firstChild;
150
+ return s.$$click = () => U(o()), s.$$mousemove = () => I(o()), s.$$mousedown = (e) => e.preventDefault(), a(l, n(p, {
151
+ get when() {
152
+ return t.icon;
153
+ },
154
+ children: (t) => (() => {
155
+ var r = N();
156
+ return a(r, n(e, {
157
+ get component() {
158
+ return t();
159
+ },
160
+ size: 14
161
+ })), r;
162
+ })()
163
+ }), null), a(l, () => t.label, null), a(s, n(p, {
164
+ get when() {
165
+ return t.detail;
166
+ },
167
+ get children() {
168
+ var e = j();
169
+ return a(e, () => t.detail), e;
170
+ }
171
+ }), null), i((e) => {
172
+ var n = x(o()), i = o(), a = String(t.value) === r.value, l = o() === F(), u = t.value === "";
173
+ return n !== e.e && c(s, "id", e.e = n), i !== e.t && c(s, "data-index", e.t = i), a !== e.a && c(s, "aria-selected", e.a = a), l !== e.o && s.classList.toggle("combobox-option-active", e.o = l), u !== e.i && s.classList.toggle("combobox-option-empty", e.i = u), e;
174
+ }, {
175
+ e: void 0,
176
+ t: void 0,
177
+ a: void 0,
178
+ o: void 0,
179
+ i: void 0
180
+ }), s;
181
+ })()
182
+ }), null), a(t, n(p, {
183
+ get when() {
184
+ return r.onAdd;
185
+ },
186
+ get children() {
187
+ var e = ee();
188
+ return e.$$click = () => U(B()), e.$$mousemove = () => I(B()), e.$$mousedown = (e) => e.preventDefault(), c(e, "aria-selected", !1), a(e, n(E, {
189
+ size: 14,
190
+ "aria-hidden": "true"
191
+ }), null), a(e, () => r.addLabel ?? "Add", null), i((t) => {
192
+ var n = x(B()), r = B(), i = B() === F();
193
+ return n !== t.e && c(e, "id", t.e = n), r !== t.t && c(e, "data-index", t.t = r), i !== t.a && e.classList.toggle("combobox-option-active", t.a = i), t;
194
+ }, {
195
+ e: void 0,
196
+ t: void 0,
197
+ a: void 0
198
+ }), e;
199
+ }
200
+ }), null), i((e) => {
201
+ var n = !!ne(), r = b();
202
+ return n !== e.e && t.classList.toggle("combobox-list-detail", e.e = n), r !== e.t && c(t, "id", e.t = r), e;
203
+ }, {
204
+ e: void 0,
205
+ t: void 0
206
+ }), t;
207
+ }
208
+ }), null), i((e) => {
209
+ var n = r.class ? `combobox ${r.class}` : "combobox", i = !!S(), a = !(S() || !R()?.detail), o = !(S() || R()?.icon === void 0), s = v(), l = S(), d = b(), f = S() && V() > 0 ? x(F()) : void 0, p = r.ariaLabel, h = r.describedBy, g = r.required, _ = r.disabled, y = r.onChange === void 0, C = r.value, w = K();
210
+ return n !== e.e && t(u, e.e = n), i !== e.t && u.classList.toggle("combobox-open", e.t = i), a !== e.a && u.classList.toggle("combobox-with-detail", e.a = a), o !== e.o && u.classList.toggle("combobox-with-icon", e.o = o), s !== e.i && c(m, "id", e.i = s), l !== e.n && c(m, "aria-expanded", e.n = l), d !== e.s && c(m, "aria-controls", e.s = d), f !== e.h && c(m, "aria-activedescendant", e.h = f), p !== e.r && c(m, "aria-label", e.r = p), h !== e.d && c(m, "aria-describedby", e.d = h), g !== e.l && (m.required = e.l = g), _ !== e.u && (m.disabled = e.u = _), y !== e.c && (m.readOnly = e.c = y), C !== e.w && c(m, "data-value", e.w = C), w !== e.m && c(m, "placeholder", e.m = w), e;
211
+ }, {
212
+ e: void 0,
213
+ t: void 0,
214
+ a: void 0,
215
+ o: void 0,
216
+ i: void 0,
217
+ n: void 0,
218
+ s: void 0,
219
+ h: void 0,
220
+ r: void 0,
221
+ d: void 0,
222
+ l: void 0,
223
+ u: void 0,
224
+ c: void 0,
225
+ w: void 0,
226
+ m: void 0
227
+ }), i(() => m.value = S() ? w() : R()?.label ?? ""), u;
228
+ })();
229
+ }
230
+ r([
231
+ "click",
232
+ "input",
233
+ "keydown",
234
+ "mousedown",
235
+ "mousemove"
236
+ ]);
237
+ //#endregion
3
238
  //#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) {
239
+ var F = /*#__PURE__*/ u("<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>"), I = /*#__PURE__*/ u("<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> "), L = /*#__PURE__*/ u("<th class=data-table-checkbox><span class=data-table-visually-hidden>Select rows</span><input type=checkbox>"), R = /*#__PURE__*/ u("<th>Actions"), z = /*#__PURE__*/ u("<div><div class=data-table-scroll><table class=data-table><thead><tr></tr></thead><tbody>"), ne = /*#__PURE__*/ u("<li><label class=data-table-customizer-option><input type=checkbox>"), B = /*#__PURE__*/ u("<button type=button class=data-table-sort><span class=data-table-sort-indicator aria-hidden=true>"), V = /*#__PURE__*/ u("<th>"), re = /*#__PURE__*/ u("<td class=data-table-checkbox><input type=checkbox>"), H = /*#__PURE__*/ u("<td>"), U = /*#__PURE__*/ u("<tr>"), W = /*#__PURE__*/ u("<p class=data-table-status>Loading…"), G = /*#__PURE__*/ u("<p class=data-table-status>No results.");
240
+ function K(e) {
6
241
  return typeof e == "function" ? e() : e;
7
242
  }
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) {
243
+ function q(e) {
244
+ let r = h(() => K(e.rows)), s = () => K(e.loading ?? !1), l = () => K(e.selected ?? []), u = e.sortKey !== void 0 || e.sortDirection !== void 0, [y, b] = g(e.defaultSort), x = () => u ? K(e.sortKey ?? void 0) : y()?.key, S = () => u ? K(e.sortDirection ?? "asc") : y()?.direction ?? "asc", C = () => K(e.showColumnCustomizer ?? !1), w = () => e.selected !== void 0 && e.onSelectionChange !== void 0, T = () => {
245
+ let t = l();
246
+ return r().length > 0 && r().every((n) => t.includes(e.getRowId(n)));
247
+ }, 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() : K(e.visibleColumns), k = h(() => new Set(O())), A = h(() => e.columns.filter((e) => e.toggleable === !1 || k().has(e.key))), te = h(() => e.columns.filter((e) => e.toggleable !== !1)), j = h(() => te().map((e) => e.key));
248
+ function M(e, t) {
14
249
  return t.sortValue ? t.sortValue(e) : e[t.key];
15
250
  }
16
- function U(e, t) {
251
+ function N(e, t) {
17
252
  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
253
  }
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)));
254
+ let P = h(() => {
255
+ let t = r(), n = x();
256
+ if (u || !n) return t;
257
+ let i = e.columns.find((e) => e.key === n);
258
+ if (!i) return t;
259
+ let a = S() === "asc" ? 1 : -1;
260
+ return [...t].sort((e, t) => a * N(M(e, i), M(t, i)));
26
261
  });
27
- function G(e) {
28
- let t = k() === e, r = t && A() === "desc" ? void 0 : {
29
- key: e,
30
- direction: t ? "desc" : "asc"
262
+ function q(t) {
263
+ let n = x() === t, r = n && S() === "desc" ? void 0 : {
264
+ key: t,
265
+ direction: n ? "desc" : "asc"
31
266
  };
32
- E || O(r), n.onSort?.(e), r || n.onSortClear?.(), n.onSortChange?.(r);
267
+ u || b(r), e.onSort?.(t), r || e.onSortClear?.(), e.onSortChange?.(r);
33
268
  }
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));
269
+ function ie(t) {
270
+ let n = new Map(e.columns.map((e, t) => [e.key, t]));
271
+ return [...t].sort((e, t) => (n.get(e) ?? 0) - (n.get(t) ?? 0));
37
272
  }
38
- function q(e) {
39
- let t = K(e);
40
- n.visibleColumns === void 0 && I(t), n.onVisibleColumnsChange?.(t);
273
+ function J(t) {
274
+ let n = ie(t);
275
+ e.visibleColumns === void 0 && ee(n), e.onVisibleColumnsChange?.(n);
41
276
  }
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)));
277
+ function ae(e, t) {
278
+ let n = new Set(O());
279
+ t ? n.add(e) : n.delete(e), J(j().filter((e) => n.has(e)));
45
280
  }
46
- function se() {
47
- q(V());
281
+ function oe() {
282
+ J(j());
48
283
  }
49
- function ce() {
50
- q(P());
284
+ function se() {
285
+ J(E());
51
286
  }
52
- let J = `${m()}-panel`, [Y, X] = p(!1), Z;
53
- d(() => {
54
- if (!Y() || !j()) return;
287
+ let Y = `${_()}-panel`, [X, Z] = g(!1), Q;
288
+ m(() => {
289
+ if (!X() || !C()) return;
55
290
  function e(e) {
56
- Z && !Z.contains(e.target) && X(!1);
291
+ Q && !Q.contains(e.target) && Z(!1);
57
292
  }
58
293
  function t(e) {
59
- e.key === "Escape" && X(!1);
294
+ e.key === "Escape" && Z(!1);
60
295
  }
61
- document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), h(() => {
296
+ document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), v(() => {
62
297
  document.removeEventListener("pointerdown", e), document.removeEventListener("keydown", t);
63
298
  });
64
299
  });
65
- let Q;
66
- d(() => {
67
- Q && (Q.indeterminate = T().length > 0 && !N());
300
+ let $;
301
+ m(() => {
302
+ $ && ($.indeterminate = l().length > 0 && !T());
68
303
  });
69
- function $(e) {
70
- n.onSelectionChange?.(e ? s().map(n.getRowId) : []);
304
+ function ce(t) {
305
+ e.onSelectionChange?.(t ? r().map(e.getRowId) : []);
71
306
  }
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);
307
+ function le(t, n) {
308
+ let r = e.getRowId(t), i = l(), a = n ? [...i, r] : i.filter((e) => e !== r);
309
+ e.onSelectionChange?.(a);
75
310
  }
76
- function ue(e) {
77
- return T().includes(n.getRowId(e));
311
+ function ue(t) {
312
+ return l().includes(e.getRowId(t));
78
313
  }
79
314
  function de(e) {
80
- return e.toggleable === !1 || R().has(e.key);
315
+ return e.toggleable === !1 || k().has(e.key);
81
316
  }
82
- function fe(e, t) {
83
- return n.getCell?.(e, t) ?? t.getValue?.(e) ?? null;
317
+ function fe(t, n) {
318
+ return e.getCell?.(t, n) ?? n.getValue?.(t) ?? null;
84
319
  }
85
320
  return (() => {
86
- var d = _(), f = d.firstChild, p = f.firstChild.firstChild, m = p.firstChild, h = p.nextSibling;
87
- return i(d, t(u, {
321
+ var l = z(), m = l.firstChild, h = m.firstChild.firstChild, g = h.firstChild, _ = h.nextSibling;
322
+ return a(l, n(p, {
88
323
  get when() {
89
- return j();
324
+ return C();
90
325
  },
91
326
  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, {
327
+ var t = I(), r = t.firstChild, o = r.firstChild;
328
+ o.firstChild.nextSibling;
329
+ var s = Q;
330
+ return typeof s == "function" ? d(s, r) : Q = r, o.$$click = () => Z((e) => !e), c(o, "aria-controls", Y), a(o, () => e.columnCustomizerLabel ?? "Columns", null), a(r, n(p, {
96
331
  get when() {
97
- return Y();
332
+ return X();
98
333
  },
99
334
  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, {
335
+ var t = F(), r = t.firstChild, o = r.nextSibling, s = o.nextSibling.firstChild, l = s.nextSibling;
336
+ return c(t, "id", Y), a(r, () => e.columnCustomizerTitle ?? "Shown columns"), a(o, n(f, {
102
337
  get each() {
103
- return n.columns;
338
+ return e.columns;
104
339
  },
105
340
  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;
341
+ var t = ne(), n = t.firstChild, r = n.firstChild;
342
+ return r.addEventListener("change", (t) => ae(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
343
  })()
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;
344
+ })), s.$$click = oe, a(s, () => e.columnCustomizerShowAllLabel ?? "Show all"), l.$$click = se, a(l, () => e.columnCustomizerResetLabel ?? "Reset"), i(() => c(t, "aria-label", e.columnCustomizerTitle ?? "Customize columns")), t;
110
345
  }
111
- }), null), r(() => o(s, "aria-expanded", Y())), e;
346
+ }), null), i(() => c(o, "aria-expanded", X())), t;
112
347
  }
113
- }), f), i(m, t(u, {
348
+ }), m), a(g, n(p, {
114
349
  get when() {
115
- return M();
350
+ return w();
116
351
  },
117
352
  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;
353
+ var t = L(), n = t.firstChild.nextSibling;
354
+ n.addEventListener("change", (e) => ce(e.currentTarget.checked));
355
+ var r = $;
356
+ return typeof r == "function" ? d(r, n) : $ = n, i(() => c(n, "aria-label", e.selectionLabel ?? "Select all rows")), i(() => n.checked = T()), t;
122
357
  }
123
- }), null), i(m, t(l, {
358
+ }), null), a(g, n(f, {
124
359
  get each() {
125
- return z();
360
+ return A();
126
361
  },
127
- children: (s) => (() => {
128
- var c = b();
129
- return i(c, t(u, {
362
+ children: (r) => (() => {
363
+ var s = V();
364
+ return a(s, n(p, {
130
365
  get when() {
131
- return a(() => !!s.sortable)() && (!E || n.onSort || n.onSortChange);
366
+ return o(() => !!r.sortable)() && (!u || e.onSort || e.onSortChange);
132
367
  },
133
368
  get fallback() {
134
- return s.label;
369
+ return r.label;
135
370
  },
136
371
  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" ? "▲" : "▼" : "";
372
+ var e = B(), t = e.firstChild;
373
+ return e.$$click = () => q(r.key), a(e, () => r.label, t), a(t, (() => {
374
+ var e = o(() => x() === r.key);
375
+ return () => e() ? S() === "asc" ? "▲" : "▼" : "";
141
376
  })()), e;
142
377
  }
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;
378
+ })), i((e) => {
379
+ var n = r.class, i = r.sortable ? x() === r.key ? S() === "asc" ? "ascending" : "descending" : "none" : void 0;
380
+ return n !== e.e && t(s, e.e = n), i !== e.t && c(s, "aria-sort", e.t = i), e;
146
381
  }, {
147
382
  e: void 0,
148
383
  t: void 0
149
- }), c;
384
+ }), s;
150
385
  })()
151
- }), null), i(m, t(u, {
386
+ }), null), a(g, n(p, {
152
387
  get when() {
153
- return n.rowActions;
388
+ return e.rowActions;
154
389
  },
155
390
  get children() {
156
- return g();
391
+ return R();
157
392
  }
158
- }), null), i(h, t(l, {
393
+ }), null), a(_, n(f, {
159
394
  get each() {
160
- return W();
395
+ return P();
161
396
  },
162
- children: (a) => (() => {
163
- var s = ie();
164
- return i(s, t(u, {
397
+ children: (r) => (() => {
398
+ var o = U();
399
+ return a(o, n(p, {
165
400
  get when() {
166
- return M();
401
+ return w();
167
402
  },
168
403
  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;
404
+ var t = re(), n = t.firstChild;
405
+ return n.addEventListener("change", (e) => le(r, e.currentTarget.checked)), i(() => c(n, "aria-label", `Select row ${e.getRowId(r)}`)), i(() => n.checked = ue(r)), t;
171
406
  }
172
- }), null), i(s, t(l, {
407
+ }), null), a(o, n(f, {
173
408
  get each() {
174
- return z();
409
+ return A();
175
410
  },
176
- children: (t) => (() => {
177
- var n = x();
178
- return i(n, () => fe(a, t)), r(() => e(n, t.class)), n;
411
+ children: (e) => (() => {
412
+ var n = H();
413
+ return a(n, () => fe(r, e)), i(() => t(n, e.class)), n;
179
414
  })()
180
- }), null), i(s, t(u, {
415
+ }), null), a(o, n(p, {
181
416
  get when() {
182
- return n.rowActions;
417
+ return e.rowActions;
183
418
  },
184
419
  get children() {
185
- var e = x();
186
- return i(e, () => n.rowActions?.(a)), e;
420
+ var t = H();
421
+ return a(t, () => e.rowActions?.(r)), t;
187
422
  }
188
- }), null), s;
423
+ }), null), o;
189
424
  })()
190
- })), i(d, t(u, {
425
+ })), a(l, n(p, {
191
426
  get when() {
192
- return w();
427
+ return s();
193
428
  },
194
429
  get children() {
195
- return n.loadingContent ?? ae();
430
+ return e.loadingContent ?? W();
196
431
  }
197
- }), null), i(d, t(u, {
432
+ }), null), a(l, n(p, {
198
433
  get when() {
199
- return a(() => !w())() && (n.empty === void 0 ? s().length === 0 : C(n.empty));
434
+ return o(() => !s())() && (e.empty === void 0 ? r().length === 0 : K(e.empty));
200
435
  },
201
436
  get children() {
202
- return n.emptyContent ?? S();
437
+ return e.emptyContent ?? G();
203
438
  }
204
- }), null), r(() => e(d, `data-table-wrap${n.class ? ` ${n.class}` : ""}`)), d;
439
+ }), null), i(() => t(l, `data-table-wrap${e.class ? ` ${e.class}` : ""}`)), l;
205
440
  })();
206
441
  }
207
- n(["click"]);
442
+ r(["click"]);
208
443
  //#endregion
209
- export { w as DataTable };
444
+ export { P as Combobox, q 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.8",
4
4
  "description": "Reusable components for SolidJS",
5
5
  "license": "MIT",
6
6
  "author": "serkonda7",
@@ -43,13 +43,14 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@biomejs/biome": "2.5.14",
46
+ "@tabler/icons-solidjs": "~3.48.0",
46
47
  "@types/bun": "1.4.2",
47
48
  "@vitest/browser-playwright": "^5.0.2",
48
- "playwright": "^1.63.0",
49
+ "playwright": "~1.63.0",
49
50
  "solid-js": "~1.9.15",
50
51
  "typescript": "~7.0.2",
51
52
  "vite": "~8.3.0",
52
53
  "vite-plugin-solid": "~2.11.14",
53
- "vitest": "^5.0.2"
54
+ "vitest": "~5.0.2"
54
55
  }
55
56
  }