@rozie-ui/data-table-solid 0.1.0
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/LICENSE +21 -0
- package/README.md +533 -0
- package/dist/index.cjs +5006 -0
- package/dist/index.d.cts +566 -0
- package/dist/index.d.mts +566 -0
- package/dist/index.mjs +4990 -0
- package/package.json +85 -0
- package/src/Column.tsx +149 -0
- package/src/DataTable.tsx +6542 -0
- package/src/DetailPanel.tsx +40 -0
- package/src/EditorCheckbox.tsx +52 -0
- package/src/EditorDate.tsx +71 -0
- package/src/EditorNumber.tsx +77 -0
- package/src/EditorSelect.tsx +79 -0
- package/src/EditorText.tsx +72 -0
- package/src/FilterNumberRange.tsx +77 -0
- package/src/FilterSelect.tsx +60 -0
- package/src/FilterText.tsx +65 -0
- package/src/GroupBar.tsx +207 -0
- package/src/Popover.tsx +416 -0
- package/src/index.ts +16 -0
- package/src/internal/middleware.ts +54 -0
- package/src/themes/base.css +102 -0
- package/src/themes/bootstrap.css +37 -0
- package/src/themes/material.css +35 -0
- package/src/themes/shadcn.css +41 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
import { Key } from '@solid-primitives/keyed';
|
|
4
|
+
import { rozieDisplay } from '@rozie/runtime-solid';
|
|
5
|
+
|
|
6
|
+
interface DetailPanelProps {
|
|
7
|
+
/**
|
|
8
|
+
* The raw row object (the `#detail` slot scope `row` = `row.original`). This drop-in walks its own enumerable keys and String-coerces each value into a key/value definition list; a null row renders an empty list.
|
|
9
|
+
*/
|
|
10
|
+
row?: (unknown) | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function DetailPanel(_props: DetailPanelProps): JSX.Element {
|
|
14
|
+
const _merged = mergeProps({ row: null }, _props);
|
|
15
|
+
const [local, attrs] = splitProps(_merged, ['row']);
|
|
16
|
+
|
|
17
|
+
// Plain setup-once helper (NOT $computed — a $computed can't be aliased; the
|
|
18
|
+
// EditorSelect plain-function lesson). Build `[{ key, value }]` from the row's own
|
|
19
|
+
// enumerable keys, String-coercing each value. A null row yields an empty list.
|
|
20
|
+
function entries() {
|
|
21
|
+
const r = local.row;
|
|
22
|
+
if (!r) return [];
|
|
23
|
+
return Object.keys(r).map((key: any) => ({
|
|
24
|
+
key,
|
|
25
|
+
value: r[key] == null ? '' : String(r[key])
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<dl class={"rdt-detail-panel"} data-rozie-s-8f65bdaa="">
|
|
32
|
+
|
|
33
|
+
<Key each={entries() as readonly any[]} by={(pair) => pair.key}>{(pair) => <div class={"rdt-detail-entry"} data-rozie-s-8f65bdaa="">
|
|
34
|
+
<dt class={"rdt-detail-key"} data-rozie-s-8f65bdaa="">{rozieDisplay(pair().key)}</dt>
|
|
35
|
+
<dd class={"rdt-detail-value"} data-rozie-s-8f65bdaa="">{rozieDisplay(pair().value)}</dd>
|
|
36
|
+
</div>}</Key>
|
|
37
|
+
</dl>
|
|
38
|
+
</>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
interface EditorCheckboxProps {
|
|
5
|
+
/**
|
|
6
|
+
* The column id (mirrors the `#editor` slot scope). Used as the input `aria-label`.
|
|
7
|
+
*/
|
|
8
|
+
columnId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The table-core column object (opaque passthrough from the `#editor` slot scope).
|
|
11
|
+
*/
|
|
12
|
+
column?: (unknown) | null;
|
|
13
|
+
/**
|
|
14
|
+
* The consumer's row data object (opaque passthrough from the `#editor` slot scope).
|
|
15
|
+
*/
|
|
16
|
+
row?: (unknown) | null;
|
|
17
|
+
/**
|
|
18
|
+
* The current cell value — coerced to a real boolean via `!!` to seed the checkbox `checked` state.
|
|
19
|
+
*/
|
|
20
|
+
value?: (unknown) | null;
|
|
21
|
+
/**
|
|
22
|
+
* `(value) => void` — commit the cell. This editor immediately commits the boolean checked state on `@change`. Null-guarded at call sites.
|
|
23
|
+
*/
|
|
24
|
+
commit?: ((...args: unknown[]) => unknown) | null;
|
|
25
|
+
/**
|
|
26
|
+
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
27
|
+
*/
|
|
28
|
+
cancel?: ((...args: unknown[]) => unknown) | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function EditorCheckbox(_props: EditorCheckboxProps): JSX.Element {
|
|
32
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null }, _props);
|
|
33
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel']);
|
|
34
|
+
|
|
35
|
+
// Immediate-commit-on-change: read .checked the global-filter way, coerce to a
|
|
36
|
+
// real boolean, and commit it directly.
|
|
37
|
+
function onChange(e: any) {
|
|
38
|
+
local.commit && local.commit(!!(e && e.target ? e.target.checked : false));
|
|
39
|
+
}
|
|
40
|
+
function onKeydown(e: any) {
|
|
41
|
+
if (e && e.key === 'Escape') {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
local.cancel && local.cancel();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<input type="checkbox" data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} checked={!!local.value} onChange={($event: Event & { currentTarget: HTMLInputElement; target: Element }) => { onChange($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} data-rozie-s-3d792482="" />
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
interface EditorDateProps {
|
|
5
|
+
/**
|
|
6
|
+
* The column id (mirrors the `#editor` slot scope). Used as the input `aria-label`.
|
|
7
|
+
*/
|
|
8
|
+
columnId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The table-core column object (opaque passthrough from the `#editor` slot scope).
|
|
11
|
+
*/
|
|
12
|
+
column?: (unknown) | null;
|
|
13
|
+
/**
|
|
14
|
+
* The consumer's row data object (opaque passthrough from the `#editor` slot scope).
|
|
15
|
+
*/
|
|
16
|
+
row?: (unknown) | null;
|
|
17
|
+
/**
|
|
18
|
+
* The current cell value the local draft seeds from (setup-once); String-coerced to an ISO `YYYY-MM-DD` string for the native date input.
|
|
19
|
+
*/
|
|
20
|
+
value?: (unknown) | null;
|
|
21
|
+
/**
|
|
22
|
+
* `(value) => void` — commit the cell with the ISO `YYYY-MM-DD` string (Enter / blur). Null-guarded at call sites.
|
|
23
|
+
*/
|
|
24
|
+
commit?: ((...args: unknown[]) => unknown) | null;
|
|
25
|
+
/**
|
|
26
|
+
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
27
|
+
*/
|
|
28
|
+
cancel?: ((...args: unknown[]) => unknown) | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function EditorDate(_props: EditorDateProps): JSX.Element {
|
|
32
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null }, _props);
|
|
33
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel']);
|
|
34
|
+
|
|
35
|
+
const [draft, setDraft] = createSignal('');
|
|
36
|
+
|
|
37
|
+
// Seed the draft once from the incoming value (setup-once). A native date input
|
|
38
|
+
// only accepts `YYYY-MM-DD`; normalize null/undefined to ''.
|
|
39
|
+
setDraft(local.value != null ? String(local.value) : '');
|
|
40
|
+
function onInput(e: any) {
|
|
41
|
+
setDraft(e && e.target ? e.target.value : '');
|
|
42
|
+
}
|
|
43
|
+
function doCommit() {
|
|
44
|
+
// commit the ISO date string the native control already produced.
|
|
45
|
+
local.commit && local.commit(draft());
|
|
46
|
+
}
|
|
47
|
+
function doCancel() {
|
|
48
|
+
local.cancel && local.cancel();
|
|
49
|
+
}
|
|
50
|
+
function onChange(e: any) {
|
|
51
|
+
setDraft(e && e.target ? e.target.value : '');
|
|
52
|
+
}
|
|
53
|
+
function onKeydown(e: any) {
|
|
54
|
+
if (e && e.key === 'Enter') {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
doCommit();
|
|
57
|
+
} else if (e && e.key === 'Escape') {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
doCancel();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function onBlur() {
|
|
63
|
+
doCommit();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<input type="date" data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} value={draft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onInput($event); }} onChange={($event: Event & { currentTarget: HTMLInputElement; target: Element }) => { onChange($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onBlur(); }} data-rozie-s-7abe1a56="" />
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
interface EditorNumberProps {
|
|
5
|
+
/**
|
|
6
|
+
* The column id (mirrors the `#editor` slot scope). Used as the input `aria-label`.
|
|
7
|
+
*/
|
|
8
|
+
columnId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The table-core column object (opaque passthrough from the `#editor` slot scope).
|
|
11
|
+
*/
|
|
12
|
+
column?: (unknown) | null;
|
|
13
|
+
/**
|
|
14
|
+
* The consumer's row data object (opaque passthrough from the `#editor` slot scope).
|
|
15
|
+
*/
|
|
16
|
+
row?: (unknown) | null;
|
|
17
|
+
/**
|
|
18
|
+
* The current cell value the local draft string seeds from (setup-once).
|
|
19
|
+
*/
|
|
20
|
+
value?: (unknown) | null;
|
|
21
|
+
/**
|
|
22
|
+
* `(value) => void` — commit the cell. The draft is coerced with `Number()` at commit time; an empty/whitespace or non-numeric draft commits `null` (never `NaN`). Null-guarded at call sites.
|
|
23
|
+
*/
|
|
24
|
+
commit?: ((...args: unknown[]) => unknown) | null;
|
|
25
|
+
/**
|
|
26
|
+
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
27
|
+
*/
|
|
28
|
+
cancel?: ((...args: unknown[]) => unknown) | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function EditorNumber(_props: EditorNumberProps): JSX.Element {
|
|
32
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null }, _props);
|
|
33
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel']);
|
|
34
|
+
|
|
35
|
+
const [draft, setDraft] = createSignal('');
|
|
36
|
+
|
|
37
|
+
// Seed the draft string once from the incoming value (setup-once).
|
|
38
|
+
setDraft(local.value != null ? String(local.value) : '');
|
|
39
|
+
function onInput(e: any) {
|
|
40
|
+
setDraft(e && e.target ? e.target.value : '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Coerce to a Number at commit time. Defensive guard: an empty/whitespace draft
|
|
44
|
+
// commits null rather than NaN (Number('') === 0 is a silent footgun); a
|
|
45
|
+
// non-numeric draft also commits null. Otherwise commit the coerced number.
|
|
46
|
+
function doCommit() {
|
|
47
|
+
if (!local.commit) return;
|
|
48
|
+
const raw = draft();
|
|
49
|
+
if (raw == null || String(raw).trim() === '') {
|
|
50
|
+
local.commit(null);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const n = Number(raw);
|
|
54
|
+
local.commit(Number.isNaN(n) ? null : n);
|
|
55
|
+
}
|
|
56
|
+
function doCancel() {
|
|
57
|
+
local.cancel && local.cancel();
|
|
58
|
+
}
|
|
59
|
+
function onKeydown(e: any) {
|
|
60
|
+
if (e && e.key === 'Enter') {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
doCommit();
|
|
63
|
+
} else if (e && e.key === 'Escape') {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
doCancel();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function onBlur() {
|
|
69
|
+
doCommit();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
<input type="number" data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} value={draft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onInput($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onBlur(); }} data-rozie-s-b2792b32="" />
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
import { Key } from '@solid-primitives/keyed';
|
|
4
|
+
import { rozieAttr, rozieDisplay } from '@rozie/runtime-solid';
|
|
5
|
+
|
|
6
|
+
interface EditorSelectProps {
|
|
7
|
+
/**
|
|
8
|
+
* The column id (mirrors the `#editor` slot scope). Used as the select `aria-label`.
|
|
9
|
+
*/
|
|
10
|
+
columnId?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The table-core column object (opaque passthrough from the `#editor` slot scope).
|
|
13
|
+
*/
|
|
14
|
+
column?: (unknown) | null;
|
|
15
|
+
/**
|
|
16
|
+
* The consumer's row data object (opaque passthrough from the `#editor` slot scope).
|
|
17
|
+
*/
|
|
18
|
+
row?: (unknown) | null;
|
|
19
|
+
/**
|
|
20
|
+
* The current cell value the local draft seeds from (setup-once); String-coerced for the `<select>` binding.
|
|
21
|
+
*/
|
|
22
|
+
value?: (unknown) | null;
|
|
23
|
+
/**
|
|
24
|
+
* `(value) => void` — commit the cell with the selected value (Enter / blur). Null-guarded at call sites.
|
|
25
|
+
*/
|
|
26
|
+
commit?: ((...args: unknown[]) => unknown) | null;
|
|
27
|
+
/**
|
|
28
|
+
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
29
|
+
*/
|
|
30
|
+
cancel?: ((...args: unknown[]) => unknown) | null;
|
|
31
|
+
/**
|
|
32
|
+
* The select options — `[{ value, label }]`. Mirrors `<Column editorOptions>`.
|
|
33
|
+
*/
|
|
34
|
+
options?: any[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function EditorSelect(_props: EditorSelectProps): JSX.Element {
|
|
38
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null, options: (() => [])() }, _props);
|
|
39
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel', 'options']);
|
|
40
|
+
|
|
41
|
+
const [draft, setDraft] = createSignal('');
|
|
42
|
+
|
|
43
|
+
// Seed the draft once from the incoming value (setup-once). Normalize null/undefined
|
|
44
|
+
// to '' so the <select> binds to a string.
|
|
45
|
+
setDraft(local.value != null ? String(local.value) : '');
|
|
46
|
+
|
|
47
|
+
// Picking/arrow-cycling an option updates the draft only — no commit.
|
|
48
|
+
function onChange(e: any) {
|
|
49
|
+
setDraft(e && e.target ? e.target.value : '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// commit/cancel are Function props (default null) — guard before calling.
|
|
53
|
+
function doCommit() {
|
|
54
|
+
local.commit && local.commit(draft());
|
|
55
|
+
}
|
|
56
|
+
function doCancel() {
|
|
57
|
+
local.cancel && local.cancel();
|
|
58
|
+
}
|
|
59
|
+
function onKeydown(e: any) {
|
|
60
|
+
if (e && e.key === 'Enter') {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
doCommit();
|
|
63
|
+
} else if (e && e.key === 'Escape') {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
doCancel();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function onBlur() {
|
|
69
|
+
doCommit();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
<select data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} value={draft()} onChange={($event: Event & { currentTarget: HTMLSelectElement; target: Element }) => { onChange($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLSelectElement; target: Element }) => { onBlur(); }} data-rozie-s-117f1a16="">
|
|
75
|
+
<Key each={local.options as readonly any[]} by={(opt) => opt.value}>{(opt) => <option value={rozieAttr(opt().value)} data-rozie-s-117f1a16="">{rozieDisplay(opt().label)}</option>}</Key>
|
|
76
|
+
</select>
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
interface EditorTextProps {
|
|
5
|
+
/**
|
|
6
|
+
* The column id (mirrors the `#editor` slot scope). Used as the input `aria-label` fallback.
|
|
7
|
+
*/
|
|
8
|
+
columnId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The table-core column object (opaque passthrough from the `#editor` slot scope).
|
|
11
|
+
*/
|
|
12
|
+
column?: (unknown) | null;
|
|
13
|
+
/**
|
|
14
|
+
* The consumer's row data object (opaque passthrough from the `#editor` slot scope).
|
|
15
|
+
*/
|
|
16
|
+
row?: (unknown) | null;
|
|
17
|
+
/**
|
|
18
|
+
* The current cell value the editor seeds its local draft from (setup-once).
|
|
19
|
+
*/
|
|
20
|
+
value?: (unknown) | null;
|
|
21
|
+
/**
|
|
22
|
+
* `(value) => void` — commit the edited cell value (from the `#editor` slot scope). Null-guarded at call sites.
|
|
23
|
+
*/
|
|
24
|
+
commit?: ((...args: unknown[]) => unknown) | null;
|
|
25
|
+
/**
|
|
26
|
+
* `() => void` — revert the edit and close the editor (from the `#editor` slot scope). Null-guarded at call sites.
|
|
27
|
+
*/
|
|
28
|
+
cancel?: ((...args: unknown[]) => unknown) | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function EditorText(_props: EditorTextProps): JSX.Element {
|
|
32
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null }, _props);
|
|
33
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel']);
|
|
34
|
+
|
|
35
|
+
const [draft, setDraft] = createSignal('');
|
|
36
|
+
|
|
37
|
+
// Seed the draft once at setup from the incoming value (setup-once, NOT in the
|
|
38
|
+
// template). Normalize null/undefined to '' so the input value binds to a string.
|
|
39
|
+
setDraft(local.value != null ? String(local.value) : '');
|
|
40
|
+
|
|
41
|
+
// Untyped handler param neutralizes to `any`, so reading e.target.value typechecks
|
|
42
|
+
// ×6 (the global-filter idiom). Never inline `$data.x = $event.target.value`.
|
|
43
|
+
function onInput(e: any) {
|
|
44
|
+
setDraft(e && e.target ? e.target.value : '');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// commit/cancel are Function props (default null) — guard before calling.
|
|
48
|
+
function doCommit() {
|
|
49
|
+
local.commit && local.commit(draft());
|
|
50
|
+
}
|
|
51
|
+
function doCancel() {
|
|
52
|
+
local.cancel && local.cancel();
|
|
53
|
+
}
|
|
54
|
+
function onKeydown(e: any) {
|
|
55
|
+
if (e && e.key === 'Enter') {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
doCommit();
|
|
58
|
+
} else if (e && e.key === 'Escape') {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
doCancel();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function onBlur() {
|
|
64
|
+
doCommit();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<input type="text" data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} value={draft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onInput($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onBlur(); }} data-rozie-s-0d17f43a="" />
|
|
70
|
+
</>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
import { rozieAttr } from '@rozie/runtime-solid';
|
|
4
|
+
|
|
5
|
+
interface FilterNumberRangeProps {
|
|
6
|
+
/**
|
|
7
|
+
* The column id (mirrors the `#filter` slot scope) — used as the filter key and the input `aria-label` base.
|
|
8
|
+
*/
|
|
9
|
+
columnId?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The table-core column object (opaque passthrough from the `#filter` slot scope).
|
|
12
|
+
*/
|
|
13
|
+
column?: (unknown) | null;
|
|
14
|
+
/**
|
|
15
|
+
* The current column filter value (`[min, max]` tuple or null) the two inputs seed from (setup-once).
|
|
16
|
+
*/
|
|
17
|
+
value?: (unknown) | null;
|
|
18
|
+
/**
|
|
19
|
+
* `(columnId, value) => void` — apply the column filter as a `[min, max]` tuple (each side coerced to a Number or `undefined`, so a one-sided range works); both empty clears the filter. Null-guarded at call sites.
|
|
20
|
+
*/
|
|
21
|
+
setFilter?: ((...args: unknown[]) => unknown) | null;
|
|
22
|
+
/**
|
|
23
|
+
* The faceted `[min, max]` bounds for this column (`[number, number]` or null) — drives the input placeholders only.
|
|
24
|
+
*/
|
|
25
|
+
minMax?: (unknown) | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function FilterNumberRange(_props: FilterNumberRangeProps): JSX.Element {
|
|
29
|
+
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null, minMax: null }, _props);
|
|
30
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'value', 'setFilter', 'minMax']);
|
|
31
|
+
|
|
32
|
+
const [minDraft, setMinDraft] = createSignal('');
|
|
33
|
+
const [maxDraft, setMaxDraft] = createSignal('');
|
|
34
|
+
|
|
35
|
+
// Seed both drafts once at setup from the incoming [min,max] tuple (setup-once).
|
|
36
|
+
setMinDraft(Array.isArray(local.value) && local.value[0] != null ? String(local.value[0]) : '');
|
|
37
|
+
setMaxDraft(Array.isArray(local.value) && local.value[1] != null ? String(local.value[1]) : '');
|
|
38
|
+
|
|
39
|
+
// Untyped handler params neutralize to `any` (the global-filter idiom).
|
|
40
|
+
function onMinInput(e: any) {
|
|
41
|
+
setMinDraft(e && e.target ? e.target.value : '');
|
|
42
|
+
}
|
|
43
|
+
function onMaxInput(e: any) {
|
|
44
|
+
setMaxDraft(e && e.target ? e.target.value : '');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Plain string-coercion functions for the placeholders (NOT $computed — the
|
|
48
|
+
// EditorSelect/listbox lesson; opaque slot-scope props rejected by strict leaf tsc).
|
|
49
|
+
function minPlaceholder() {
|
|
50
|
+
return Array.isArray(local.minMax) && local.minMax[0] != null ? String(local.minMax[0]) : '';
|
|
51
|
+
}
|
|
52
|
+
function maxPlaceholder() {
|
|
53
|
+
return Array.isArray(local.minMax) && local.minMax[1] != null ? String(local.minMax[1]) : '';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Convert a draft to a Number or undefined (empty string → undefined so a
|
|
57
|
+
// one-sided range works). Both undefined → clear the filter.
|
|
58
|
+
function applyRange() {
|
|
59
|
+
const minNum = minDraft() === '' ? undefined : Number(minDraft());
|
|
60
|
+
const maxNum = maxDraft() === '' ? undefined : Number(maxDraft());
|
|
61
|
+
if (minNum === undefined && maxNum === undefined) {
|
|
62
|
+
local.setFilter && local.setFilter(local.columnId, '');
|
|
63
|
+
} else {
|
|
64
|
+
local.setFilter && local.setFilter(local.columnId, [minNum, maxNum]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<>
|
|
70
|
+
<span style={{ display: "flex", "align-items": "center" }} data-rozie-s-97b2c090="">
|
|
71
|
+
<input part="col-filter" type="number" aria-label={rozieAttr(local.columnId + ' min')} class={"rdt-col-filter"} placeholder={rozieAttr(minPlaceholder())} value={minDraft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onMinInput($event); }} onChange={($event: Event & { currentTarget: HTMLInputElement; target: Element }) => { applyRange(); }} data-rozie-s-97b2c090="" />
|
|
72
|
+
<span data-rozie-s-97b2c090=""> - </span>
|
|
73
|
+
<input part="col-filter" type="number" aria-label={rozieAttr(local.columnId + ' max')} class={"rdt-col-filter"} placeholder={rozieAttr(maxPlaceholder())} value={maxDraft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onMaxInput($event); }} onChange={($event: Event & { currentTarget: HTMLInputElement; target: Element }) => { applyRange(); }} data-rozie-s-97b2c090="" />
|
|
74
|
+
</span>
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
import { Key } from '@solid-primitives/keyed';
|
|
4
|
+
import { rozieAttr, rozieDisplay } from '@rozie/runtime-solid';
|
|
5
|
+
|
|
6
|
+
interface FilterSelectProps {
|
|
7
|
+
/**
|
|
8
|
+
* The column id (mirrors the `#filter` slot scope) — used as the filter key and the select `aria-label`.
|
|
9
|
+
*/
|
|
10
|
+
columnId?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The table-core column object (opaque passthrough from the `#filter` slot scope).
|
|
13
|
+
*/
|
|
14
|
+
column?: (unknown) | null;
|
|
15
|
+
/**
|
|
16
|
+
* The current column filter value the select seeds from (String-coerced).
|
|
17
|
+
*/
|
|
18
|
+
value?: (unknown) | null;
|
|
19
|
+
/**
|
|
20
|
+
* `(columnId, value) => void` — apply the column filter on change; the leading empty "All" option clears it. Null-guarded at call sites.
|
|
21
|
+
*/
|
|
22
|
+
setFilter?: ((...args: unknown[]) => unknown) | null;
|
|
23
|
+
/**
|
|
24
|
+
* The faceted distinct keys for this column (cross-filtered, keys only — no occurrence counts) used to build the `<option>` list.
|
|
25
|
+
*/
|
|
26
|
+
uniqueValues?: any[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default function FilterSelect(_props: FilterSelectProps): JSX.Element {
|
|
30
|
+
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null, uniqueValues: (() => [])() }, _props);
|
|
31
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'value', 'setFilter', 'uniqueValues']);
|
|
32
|
+
|
|
33
|
+
// The <select> value binding coerced to a string. $props.value is typed `unknown`
|
|
34
|
+
// (opaque slot-scope), which the strict bundled-leaf tsc rejects against the native
|
|
35
|
+
// select `value` type on React/Solid — the fix is a plain function returning a
|
|
36
|
+
// string (uniform ×6, NOT a $computed; the EditorSelect/listbox value lesson).
|
|
37
|
+
function selectValue() {
|
|
38
|
+
return local.value != null ? String(local.value) : '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Immediate-apply-on-change: read the selected value the global-filter way. An
|
|
42
|
+
// empty value (the leading "All" option) clears the column filter.
|
|
43
|
+
function onChange(e: any) {
|
|
44
|
+
const v = e && e.target ? e.target.value : '';
|
|
45
|
+
if (v === '') {
|
|
46
|
+
local.setFilter && local.setFilter(local.columnId, '');
|
|
47
|
+
} else {
|
|
48
|
+
local.setFilter && local.setFilter(local.columnId, v);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<select part="col-filter" aria-label={local.columnId} class={"rdt-col-filter"} value={selectValue()} onChange={($event: Event & { currentTarget: HTMLSelectElement; target: Element }) => { onChange($event); }} data-rozie-s-d75b42b2="">
|
|
55
|
+
<option value="" data-rozie-s-d75b42b2="">All</option>
|
|
56
|
+
<Key each={local.uniqueValues as readonly any[]} by={(opt) => opt}>{(opt) => <option value={rozieAttr(opt())} data-rozie-s-d75b42b2="">{rozieDisplay(opt())}</option>}</Key>
|
|
57
|
+
</select>
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { JSX } from 'solid-js';
|
|
2
|
+
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
interface FilterTextProps {
|
|
5
|
+
/**
|
|
6
|
+
* The column id (mirrors the `#filter` slot scope) — used as the filter key and the input `aria-label`.
|
|
7
|
+
*/
|
|
8
|
+
columnId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The table-core column object (opaque passthrough from the `#filter` slot scope).
|
|
11
|
+
*/
|
|
12
|
+
column?: (unknown) | null;
|
|
13
|
+
/**
|
|
14
|
+
* The current column filter value the local draft seeds from (setup-once).
|
|
15
|
+
*/
|
|
16
|
+
value?: (unknown) | null;
|
|
17
|
+
/**
|
|
18
|
+
* `(columnId, value) => void` — apply the column filter (Enter / blur applies, Escape clears). Null-guarded at call sites.
|
|
19
|
+
*/
|
|
20
|
+
setFilter?: ((...args: unknown[]) => unknown) | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function FilterText(_props: FilterTextProps): JSX.Element {
|
|
24
|
+
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null }, _props);
|
|
25
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'value', 'setFilter']);
|
|
26
|
+
|
|
27
|
+
const [draft, setDraft] = createSignal('');
|
|
28
|
+
|
|
29
|
+
// Seed the draft once at setup from the incoming value (setup-once, NOT in the
|
|
30
|
+
// template). Normalize null/undefined to '' so the input value binds to a string.
|
|
31
|
+
setDraft(local.value != null ? String(local.value) : '');
|
|
32
|
+
|
|
33
|
+
// Untyped handler param neutralizes to `any`, so reading e.target.value typechecks
|
|
34
|
+
// ×6 (the global-filter idiom). Never inline `$data.x = $event.target.value`.
|
|
35
|
+
function onInput(e: any) {
|
|
36
|
+
setDraft(e && e.target ? e.target.value : '');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// setFilter is a Function prop (default null) — guard before calling.
|
|
40
|
+
function applyFilter() {
|
|
41
|
+
local.setFilter && local.setFilter(local.columnId, draft());
|
|
42
|
+
}
|
|
43
|
+
function clearFilter() {
|
|
44
|
+
setDraft('');
|
|
45
|
+
local.setFilter && local.setFilter(local.columnId, '');
|
|
46
|
+
}
|
|
47
|
+
function onKeydown(e: any) {
|
|
48
|
+
if (e && e.key === 'Enter') {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
applyFilter();
|
|
51
|
+
} else if (e && e.key === 'Escape') {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
clearFilter();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function onBlur() {
|
|
57
|
+
applyFilter();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<input part="col-filter" type="text" aria-label={local.columnId} class={"rdt-col-filter"} value={draft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onInput($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onBlur(); }} data-rozie-s-18cbb44e="" />
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|