@rebasepro/ui 0.7.0 → 0.9.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/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +13 -0
- package/dist/components/VirtualTable/fields/VirtualTableInput.d.ts +10 -0
- package/dist/components/VirtualTable/fields/VirtualTableNumberInput.d.ts +9 -0
- package/dist/components/VirtualTable/fields/VirtualTableSelect.d.ts +17 -0
- package/dist/components/VirtualTable/fields/VirtualTableSwitch.d.ts +8 -0
- package/dist/components/VirtualTable/index.d.ts +7 -0
- package/dist/components/VirtualTable/selection/SelectionContext.d.ts +11 -0
- package/dist/components/VirtualTable/selection/SelectionStore.d.ts +14 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.es.js +2411 -2154
- package/dist/index.es.js.map +1 -1
- package/package.json +1 -1
- package/src/components/VirtualTable/VirtualTableProps.tsx +7 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +32 -0
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +82 -0
- package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +71 -0
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +107 -0
- package/src/components/VirtualTable/fields/VirtualTableSwitch.tsx +28 -0
- package/src/components/VirtualTable/index.tsx +7 -0
- package/src/components/VirtualTable/selection/SelectionContext.tsx +36 -0
- package/src/components/VirtualTable/selection/SelectionStore.ts +54 -0
- package/src/index.ts +42 -4
package/package.json
CHANGED
|
@@ -288,7 +288,13 @@ export type WhereFilterOp =
|
|
|
288
288
|
| "array-contains"
|
|
289
289
|
| "in"
|
|
290
290
|
| "not-in"
|
|
291
|
-
| "array-contains-any"
|
|
291
|
+
| "array-contains-any"
|
|
292
|
+
| "like"
|
|
293
|
+
| "ilike"
|
|
294
|
+
| "not-like"
|
|
295
|
+
| "not-ilike"
|
|
296
|
+
| "is-null"
|
|
297
|
+
| "is-not-null";
|
|
292
298
|
|
|
293
299
|
export type FilterValues<Key extends string> =
|
|
294
300
|
Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cls } from "../../../util";
|
|
3
|
+
import { focusedDisabled } from "../../../styles";
|
|
4
|
+
import { DateTimeField } from "../../DateTimeField";
|
|
5
|
+
|
|
6
|
+
export function VirtualTableDateField(props: {
|
|
7
|
+
name?: string;
|
|
8
|
+
error?: Error;
|
|
9
|
+
mode?: "date" | "date_time";
|
|
10
|
+
timezone?: string;
|
|
11
|
+
internalValue: Date | undefined | null;
|
|
12
|
+
updateValue: (newValue: (Date | null)) => void;
|
|
13
|
+
focused: boolean;
|
|
14
|
+
disabled: boolean;
|
|
15
|
+
locale?: string;
|
|
16
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
|
17
|
+
}) {
|
|
18
|
+
const { disabled, error, mode, timezone, internalValue, updateValue, locale } = props;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<DateTimeField
|
|
22
|
+
value={internalValue ?? undefined}
|
|
23
|
+
onChange={(dateValue) => updateValue(dateValue ?? null)}
|
|
24
|
+
invisible={true}
|
|
25
|
+
inputClassName={cls("w-full h-full", focusedDisabled)}
|
|
26
|
+
className={cls("w-full h-full", focusedDisabled)}
|
|
27
|
+
mode={mode}
|
|
28
|
+
timezone={timezone}
|
|
29
|
+
locale={locale}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useDebouncedCallback } from "../../../hooks/useDebouncedCallback";
|
|
3
|
+
import { focusedDisabled } from "../../../styles";
|
|
4
|
+
import { TextareaAutosize } from "../../TextareaAutosize";
|
|
5
|
+
|
|
6
|
+
export function VirtualTableInput(props: {
|
|
7
|
+
error?: Error;
|
|
8
|
+
value: string;
|
|
9
|
+
multiline?: boolean;
|
|
10
|
+
focused: boolean;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
updateValue: (newValue: (string | null)) => void;
|
|
13
|
+
onBlur?: () => void;
|
|
14
|
+
}) {
|
|
15
|
+
const ref = React.useRef<HTMLTextAreaElement>(null);
|
|
16
|
+
const { disabled, value, multiline = false, updateValue, focused } = props;
|
|
17
|
+
const prevValue = useRef<string | null>(value);
|
|
18
|
+
const [internalValue, setInternalValue] = useState<typeof value>(value);
|
|
19
|
+
const focusedState = useRef<boolean>(false);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (prevValue.current !== value && value !== internalValue)
|
|
23
|
+
setInternalValue(value);
|
|
24
|
+
prevValue.current = value;
|
|
25
|
+
}, [value]);
|
|
26
|
+
|
|
27
|
+
const doUpdate = React.useCallback(() => {
|
|
28
|
+
const emptyInitialValue = !value;
|
|
29
|
+
if (emptyInitialValue && !internalValue) return;
|
|
30
|
+
if (internalValue !== value && internalValue !== prevValue.current) {
|
|
31
|
+
prevValue.current = internalValue;
|
|
32
|
+
updateValue(internalValue);
|
|
33
|
+
}
|
|
34
|
+
}, [internalValue, updateValue, value]);
|
|
35
|
+
|
|
36
|
+
useDebouncedCallback(internalValue, doUpdate, !focused, 400);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (ref.current && focused && !focusedState.current) {
|
|
40
|
+
focusedState.current = true;
|
|
41
|
+
ref.current.focus({ preventScroll: true });
|
|
42
|
+
ref.current.selectionStart = ref.current.value.length;
|
|
43
|
+
ref.current.selectionEnd = ref.current.value.length;
|
|
44
|
+
} else {
|
|
45
|
+
focusedState.current = focused;
|
|
46
|
+
}
|
|
47
|
+
}, [focused, ref]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<TextareaAutosize
|
|
51
|
+
className={focusedDisabled}
|
|
52
|
+
ref={ref}
|
|
53
|
+
style={{
|
|
54
|
+
padding: 0,
|
|
55
|
+
margin: 0,
|
|
56
|
+
width: "100%",
|
|
57
|
+
color: "unset",
|
|
58
|
+
fontWeight: "unset",
|
|
59
|
+
fontSize: "unset",
|
|
60
|
+
fontFamily: "unset",
|
|
61
|
+
background: "unset",
|
|
62
|
+
border: "unset",
|
|
63
|
+
resize: "none",
|
|
64
|
+
outline: "none"
|
|
65
|
+
}}
|
|
66
|
+
value={internalValue ?? ""}
|
|
67
|
+
onChange={(evt) => {
|
|
68
|
+
const newValue = evt.target.value as string;
|
|
69
|
+
if (multiline || !newValue.endsWith("\n"))
|
|
70
|
+
setInternalValue(newValue);
|
|
71
|
+
}}
|
|
72
|
+
onFocus={() => {
|
|
73
|
+
focusedState.current = true;
|
|
74
|
+
}}
|
|
75
|
+
onBlur={() => {
|
|
76
|
+
focusedState.current = false;
|
|
77
|
+
doUpdate();
|
|
78
|
+
if (props.onBlur) props.onBlur();
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useDebouncedCallback } from "../../../hooks/useDebouncedCallback";
|
|
3
|
+
import { cls } from "../../../util";
|
|
4
|
+
import { focusedDisabled } from "../../../styles";
|
|
5
|
+
import { TextField } from "../../TextField";
|
|
6
|
+
|
|
7
|
+
export function VirtualTableNumberInput(props: {
|
|
8
|
+
error?: Error;
|
|
9
|
+
value: number;
|
|
10
|
+
align?: "right" | "left" | "center";
|
|
11
|
+
updateValue: (newValue: (number | null)) => void;
|
|
12
|
+
focused: boolean;
|
|
13
|
+
disabled: boolean;
|
|
14
|
+
}) {
|
|
15
|
+
const { align = "left", value, updateValue, focused } = props;
|
|
16
|
+
const propStringValue = (value && typeof value === "number") ? value.toString() : "";
|
|
17
|
+
const [internalValue, setInternalValue] = useState<string | null>(propStringValue);
|
|
18
|
+
const prevValue = useRef<number | null>(value);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (prevValue.current !== value && String(value) !== internalValue)
|
|
22
|
+
setInternalValue(value ? value.toString() : null);
|
|
23
|
+
prevValue.current = value;
|
|
24
|
+
}, [value]);
|
|
25
|
+
|
|
26
|
+
const doUpdate = React.useCallback(() => {
|
|
27
|
+
if (internalValue !== propStringValue) {
|
|
28
|
+
if (internalValue !== undefined && internalValue !== null) {
|
|
29
|
+
const numberValue = parseFloat(internalValue);
|
|
30
|
+
if (isNaN(numberValue)) return;
|
|
31
|
+
updateValue(numberValue);
|
|
32
|
+
} else {
|
|
33
|
+
updateValue(null);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}, [internalValue, value]);
|
|
37
|
+
|
|
38
|
+
useDebouncedCallback(internalValue, doUpdate, !focused, 400);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!focused && propStringValue !== internalValue)
|
|
42
|
+
setInternalValue(value !== undefined && value !== null ? value.toString() : null);
|
|
43
|
+
}, [value, focused]);
|
|
44
|
+
|
|
45
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (inputRef.current && focused) {
|
|
49
|
+
inputRef.current.focus({ preventScroll: true });
|
|
50
|
+
}
|
|
51
|
+
}, [focused, inputRef]);
|
|
52
|
+
|
|
53
|
+
const regexp = /^-?[0-9]+[,.]?[0-9]*$/;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<TextField
|
|
57
|
+
inputRef={inputRef}
|
|
58
|
+
invisible={true}
|
|
59
|
+
size="small"
|
|
60
|
+
className="w-full"
|
|
61
|
+
inputClassName={cls("p-0 m-0 bg-transparent border-none outline-hidden font-normal leading-normal text-unset", focusedDisabled)}
|
|
62
|
+
inputStyle={{ textAlign: align }}
|
|
63
|
+
value={internalValue ?? ""}
|
|
64
|
+
onChange={(evt) => {
|
|
65
|
+
const newValue = evt.target.value.replace(",", ".");
|
|
66
|
+
if (newValue.length === 0) setInternalValue(null);
|
|
67
|
+
if (regexp.test(newValue) || newValue.startsWith("-")) setInternalValue(newValue);
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import { Select, SelectItem } from "../../Select";
|
|
3
|
+
import { MultiSelect, MultiSelectItem } from "../../MultiSelect";
|
|
4
|
+
import { Chip } from "../../Chip";
|
|
5
|
+
|
|
6
|
+
export interface VirtualTableSelectOption {
|
|
7
|
+
value: string | number;
|
|
8
|
+
label: string;
|
|
9
|
+
color?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function VirtualTableSelect(props: {
|
|
13
|
+
options: VirtualTableSelectOption[];
|
|
14
|
+
error?: Error;
|
|
15
|
+
multiple?: boolean;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
small?: boolean;
|
|
18
|
+
value: string | number | string[] | number[] | undefined;
|
|
19
|
+
updateValue: (newValue: (string | number | string[] | number[] | null)) => void;
|
|
20
|
+
focused: boolean;
|
|
21
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
|
22
|
+
}) {
|
|
23
|
+
const { options, value, disabled, small = false, focused, updateValue, multiple = false } = props;
|
|
24
|
+
const validValue = (Array.isArray(value) && multiple) || (!Array.isArray(value) && !multiple);
|
|
25
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (ref.current && focused) {
|
|
29
|
+
ref.current?.focus({ preventScroll: true });
|
|
30
|
+
}
|
|
31
|
+
}, [focused, ref]);
|
|
32
|
+
|
|
33
|
+
const onChange = useCallback((updatedValue: string | string[]) => {
|
|
34
|
+
if (multiple) {
|
|
35
|
+
const isNumber = options.some(o => typeof o.value === "number");
|
|
36
|
+
const newValue = (updatedValue as string[]).map((v) => {
|
|
37
|
+
const opt = options.find(o => String(o.value) === v);
|
|
38
|
+
return typeof opt?.value === "number" ? parseFloat(v) : v;
|
|
39
|
+
});
|
|
40
|
+
if (isNumber) {
|
|
41
|
+
updateValue(newValue as number[]);
|
|
42
|
+
} else {
|
|
43
|
+
updateValue(newValue as string[]);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
const opt = options.find(o => String(o.value) === updatedValue);
|
|
47
|
+
updateValue(typeof opt?.value === "number" ? parseFloat(updatedValue as string) : (updatedValue as string || null));
|
|
48
|
+
}
|
|
49
|
+
}, [multiple, updateValue, options]);
|
|
50
|
+
|
|
51
|
+
const renderValue = (val?: string | number) => {
|
|
52
|
+
const option = options.find(o => o.value === val);
|
|
53
|
+
return (
|
|
54
|
+
<Chip size={small ? "small" : "medium"}>
|
|
55
|
+
{option ? option.label : String(val)}
|
|
56
|
+
</Chip>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleOpenChange = useCallback((open: boolean) => {
|
|
61
|
+
if (!open && ref.current) {
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
ref.current?.focus({ preventScroll: true });
|
|
64
|
+
}, 0);
|
|
65
|
+
}
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
return multiple ? (
|
|
69
|
+
<MultiSelect
|
|
70
|
+
inputRef={ref}
|
|
71
|
+
className="w-full h-full p-0 bg-transparent outline-none"
|
|
72
|
+
position={"item-aligned"}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
includeClear={false}
|
|
75
|
+
useChips={false}
|
|
76
|
+
value={validValue ? (value as (string | number)[]).map(v => String(v)) : []}
|
|
77
|
+
onValueChange={onChange}
|
|
78
|
+
onOpenChange={handleOpenChange}
|
|
79
|
+
>
|
|
80
|
+
{options.map((opt) => (
|
|
81
|
+
<MultiSelectItem key={String(opt.value)} value={String(opt.value)}>
|
|
82
|
+
<Chip size={small ? "small" : "medium"}>{opt.label}</Chip>
|
|
83
|
+
</MultiSelectItem>
|
|
84
|
+
))}
|
|
85
|
+
</MultiSelect>
|
|
86
|
+
) : (
|
|
87
|
+
<Select
|
|
88
|
+
inputRef={ref}
|
|
89
|
+
fullWidth
|
|
90
|
+
className="w-full h-full p-0 bg-transparent outline-none [&_button]:ring-0 [&_button]:ring-offset-0"
|
|
91
|
+
inputClassName="ring-0 ring-offset-0 focus:ring-0 focus-visible:ring-0 outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-offset-0"
|
|
92
|
+
position={"item-aligned"}
|
|
93
|
+
disabled={disabled}
|
|
94
|
+
padding={false}
|
|
95
|
+
value={validValue ? String(value) : ""}
|
|
96
|
+
onValueChange={onChange}
|
|
97
|
+
onOpenChange={handleOpenChange}
|
|
98
|
+
renderValue={renderValue}
|
|
99
|
+
>
|
|
100
|
+
{options.map((opt) => (
|
|
101
|
+
<SelectItem key={String(opt.value)} value={String(opt.value)}>
|
|
102
|
+
<Chip size={small ? "small" : "medium"}>{opt.label}</Chip>
|
|
103
|
+
</SelectItem>
|
|
104
|
+
))}
|
|
105
|
+
</Select>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { BooleanSwitch } from "../../BooleanSwitch";
|
|
3
|
+
|
|
4
|
+
export function VirtualTableSwitch(props: {
|
|
5
|
+
error?: Error;
|
|
6
|
+
internalValue?: boolean;
|
|
7
|
+
focused: boolean;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
updateValue: (newValue: (boolean | null)) => void;
|
|
10
|
+
}) {
|
|
11
|
+
const { internalValue, updateValue, focused } = props;
|
|
12
|
+
const ref = React.useRef<HTMLButtonElement>(null);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (ref.current && focused) {
|
|
16
|
+
ref.current.focus({ preventScroll: true });
|
|
17
|
+
}
|
|
18
|
+
}, [focused, ref]);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<BooleanSwitch
|
|
22
|
+
ref={ref}
|
|
23
|
+
size={"small"}
|
|
24
|
+
value={Boolean(internalValue)}
|
|
25
|
+
onValueChange={updateValue}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
export { VirtualTable } from "./VirtualTable";
|
|
2
2
|
export * from "./VirtualTableProps";
|
|
3
3
|
export type { FilterFormFieldProps } from "./VirtualTableHeader";
|
|
4
|
+
export * from "./selection/SelectionStore";
|
|
5
|
+
export * from "./selection/SelectionContext";
|
|
6
|
+
export * from "./fields/VirtualTableInput";
|
|
7
|
+
export * from "./fields/VirtualTableNumberInput";
|
|
8
|
+
export * from "./fields/VirtualTableSwitch";
|
|
9
|
+
export * from "./fields/VirtualTableDateField";
|
|
10
|
+
export * from "./fields/VirtualTableSelect";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { createContext, useContext } from "react";
|
|
2
|
+
import { VirtualTableSelectionStore, SelectedCell } from "./SelectionStore";
|
|
3
|
+
|
|
4
|
+
export interface VirtualTableSelectionContextProps<T extends SelectedCell = SelectedCell> {
|
|
5
|
+
selectionStore: VirtualTableSelectionStore<T>;
|
|
6
|
+
select: (cell: T | undefined) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const SelectionContext = createContext<VirtualTableSelectionContextProps<any> | null>(null);
|
|
10
|
+
|
|
11
|
+
export const VirtualTableSelectionProvider = <T extends SelectedCell = SelectedCell>({
|
|
12
|
+
store,
|
|
13
|
+
children
|
|
14
|
+
}: {
|
|
15
|
+
store: VirtualTableSelectionStore<T>;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
}) => {
|
|
18
|
+
const value = React.useMemo(() => ({
|
|
19
|
+
selectionStore: store,
|
|
20
|
+
select: store.select
|
|
21
|
+
}), [store]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<SelectionContext.Provider value={value}>
|
|
25
|
+
{children}
|
|
26
|
+
</SelectionContext.Provider>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const useVirtualTableSelection = <T extends SelectedCell = SelectedCell>() => {
|
|
31
|
+
const context = useContext(SelectionContext);
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error("useVirtualTableSelection must be used within a VirtualTableSelectionProvider");
|
|
34
|
+
}
|
|
35
|
+
return context as VirtualTableSelectionContextProps<T>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useSyncExternalStore, useCallback, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export interface SelectedCell {
|
|
4
|
+
columnKey: string;
|
|
5
|
+
id: string | number;
|
|
6
|
+
cellRect?: DOMRect;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createVirtualTableSelectionStore<T extends SelectedCell = SelectedCell>() {
|
|
12
|
+
let selectedCell: T | undefined = undefined;
|
|
13
|
+
const listeners = new Set<() => void>();
|
|
14
|
+
|
|
15
|
+
function getEntity(): T | undefined {
|
|
16
|
+
return selectedCell;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function subscribe(listener: () => void): () => void {
|
|
20
|
+
listeners.add(listener);
|
|
21
|
+
return () => listeners.delete(listener);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function select(cell: T | undefined) {
|
|
25
|
+
selectedCell = cell;
|
|
26
|
+
listeners.forEach(l => l());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
getEntity,
|
|
31
|
+
subscribe,
|
|
32
|
+
select
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type VirtualTableSelectionStore<T extends SelectedCell = SelectedCell> = ReturnType<typeof createVirtualTableSelectionStore<T>>;
|
|
37
|
+
|
|
38
|
+
export function useVirtualTableCellSelected<T extends SelectedCell = SelectedCell>(
|
|
39
|
+
store: VirtualTableSelectionStore<T>,
|
|
40
|
+
columnKey: string,
|
|
41
|
+
id: string | number
|
|
42
|
+
): boolean {
|
|
43
|
+
const selectorRef = useRef({ columnKey, id });
|
|
44
|
+
selectorRef.current = { columnKey, id };
|
|
45
|
+
|
|
46
|
+
const getEntity = useCallback(() => {
|
|
47
|
+
const cell = store.getEntity();
|
|
48
|
+
if (!cell) return false;
|
|
49
|
+
const s = selectorRef.current;
|
|
50
|
+
return cell.columnKey === s.columnKey && cell.id === s.id;
|
|
51
|
+
}, [store]);
|
|
52
|
+
|
|
53
|
+
return useSyncExternalStore(store.subscribe, getEntity, getEntity);
|
|
54
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,45 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Components
|
|
3
|
+
// =============================================================================
|
|
1
4
|
export * from "./components";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Views
|
|
8
|
+
// =============================================================================
|
|
6
9
|
export * from "./views";
|
|
7
10
|
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Icons
|
|
13
|
+
// =============================================================================
|
|
14
|
+
export * from "./icons";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Design System Utilities
|
|
18
|
+
// =============================================================================
|
|
19
|
+
export * from "./styles";
|
|
20
|
+
export { cls } from "./util/cls";
|
|
21
|
+
export { CHIP_COLORS, getColorSchemeForKey, getColorSchemeForSeed } from "./util/chip_colors";
|
|
22
|
+
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Public Hooks
|
|
25
|
+
// =============================================================================
|
|
26
|
+
export { useOutsideAlerter } from "./hooks/useOutsideAlerter";
|
|
27
|
+
export { useDebouncedCallback } from "./hooks/useDebouncedCallback";
|
|
28
|
+
export { useDebounceCallback } from "./hooks/useDebounceCallback";
|
|
29
|
+
export { useDebounceValue } from "./hooks/useDebounceValue";
|
|
30
|
+
export { useInjectStyles } from "./hooks/useInjectStyles";
|
|
31
|
+
export { PortalContainerProvider, usePortalContainer } from "./hooks/PortalContainerContext";
|
|
32
|
+
export type { PortalContainerContextType, PortalContainerProviderProps } from "./hooks/PortalContainerContext";
|
|
33
|
+
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// Lower-level utilities (used by framework packages)
|
|
36
|
+
// =============================================================================
|
|
37
|
+
export { debounce } from "./util/debounce";
|
|
38
|
+
export type { Cancelable } from "./util/debounce";
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// NOT exported (internal plumbing — import directly if needed):
|
|
42
|
+
//
|
|
43
|
+
// ./util/hash – hashString
|
|
44
|
+
// ./util/key_to_icon_component – keyToIconComponent
|
|
45
|
+
// =============================================================================
|