@rebasepro/ui 0.2.5 → 0.4.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/Card.d.ts +2 -3
- package/dist/components/FilterChip.d.ts +2 -10
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +8 -2
- package/dist/index.es.js +112 -94
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +111 -93
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -3
- package/src/components/Card.tsx +1 -1
- package/src/components/DebouncedTextField.tsx +57 -16
- package/src/components/FilterChip.tsx +8 -13
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +3 -2
- package/src/components/VirtualTable/VirtualTableProps.tsx +21 -12
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -71,8 +71,7 @@
|
|
|
71
71
|
"react-dropzone": "^14.4.1",
|
|
72
72
|
"react-use-measure": "^2.1.7",
|
|
73
73
|
"react-window": "^1.8.11",
|
|
74
|
-
"tailwind-merge": "^2.6.1"
|
|
75
|
-
"@rebasepro/types": "0.2.5"
|
|
74
|
+
"tailwind-merge": "^2.6.1"
|
|
76
75
|
},
|
|
77
76
|
"peerDependencies": {
|
|
78
77
|
"react": ">=19.0.0",
|
package/src/components/Card.tsx
CHANGED
|
@@ -1,33 +1,74 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, { ChangeEvent, useCallback,
|
|
2
|
+
import React, { ChangeEvent, useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { TextField, TextFieldProps } from "./index";
|
|
4
4
|
|
|
5
5
|
export function DebouncedTextField<T extends string | number>(props: TextFieldProps<T>) {
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const deferredValue = useDeferredValue(internalValue);
|
|
7
|
+
const [internalValue, setInternalValue] = useState<T | string>(props.value ?? "");
|
|
8
|
+
const lastSentValueRef = useRef<T | string>(props.value ?? "");
|
|
9
|
+
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
11
10
|
|
|
11
|
+
// Sync state with props.value when it changes externally
|
|
12
12
|
useEffect(() => {
|
|
13
|
-
|
|
13
|
+
const externalValue = props.value ?? "";
|
|
14
|
+
if (externalValue !== lastSentValueRef.current) {
|
|
15
|
+
setInternalValue(externalValue);
|
|
16
|
+
lastSentValueRef.current = externalValue;
|
|
17
|
+
}
|
|
14
18
|
}, [props.value]);
|
|
15
19
|
|
|
20
|
+
// Cleanup timer on unmount
|
|
16
21
|
useEffect(() => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
return () => {
|
|
23
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
24
|
+
};
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
const flushChange = useCallback((value: T | string, event?: ChangeEvent<any>) => {
|
|
28
|
+
if (timerRef.current) {
|
|
29
|
+
clearTimeout(timerRef.current);
|
|
30
|
+
timerRef.current = undefined;
|
|
31
|
+
}
|
|
32
|
+
if (value !== props.value && props.onChange) {
|
|
33
|
+
lastSentValueRef.current = value;
|
|
34
|
+
const e = {
|
|
35
|
+
...event,
|
|
36
|
+
target: {
|
|
37
|
+
...event?.target,
|
|
38
|
+
value: value,
|
|
39
|
+
name: props.name
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
props.onChange(e as any);
|
|
22
43
|
}
|
|
23
|
-
}, [
|
|
44
|
+
}, [props.value, props.onChange, props.name]);
|
|
24
45
|
|
|
25
46
|
const internalOnChange = useCallback((event: ChangeEvent<any>) => {
|
|
26
|
-
|
|
27
|
-
setInternalValue(
|
|
28
|
-
|
|
47
|
+
const newValue = event.target.value;
|
|
48
|
+
setInternalValue(newValue);
|
|
49
|
+
|
|
50
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
51
|
+
|
|
52
|
+
const eventCopy = {
|
|
53
|
+
...event,
|
|
54
|
+
target: {
|
|
55
|
+
...event?.target,
|
|
56
|
+
name: event?.target?.name
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
timerRef.current = setTimeout(() => {
|
|
61
|
+
flushChange(newValue, eventCopy as any);
|
|
62
|
+
}, 150);
|
|
63
|
+
}, [flushChange]);
|
|
64
|
+
|
|
65
|
+
const internalOnBlur = useCallback((event: React.FocusEvent<any>) => {
|
|
66
|
+
flushChange(internalValue, event as any);
|
|
67
|
+
props.onBlur?.(event);
|
|
68
|
+
}, [internalValue, flushChange, props.onBlur]);
|
|
29
69
|
|
|
30
70
|
return <TextField {...props}
|
|
31
71
|
onChange={internalOnChange}
|
|
32
|
-
|
|
72
|
+
onBlur={internalOnBlur}
|
|
73
|
+
value={internalValue as T}/>
|
|
33
74
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { cls } from "../util";
|
|
3
3
|
|
|
4
|
-
export interface FilterChipProps {
|
|
4
|
+
export interface FilterChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
5
5
|
/**
|
|
6
6
|
* The text label displayed on the chip.
|
|
7
7
|
*/
|
|
@@ -10,10 +10,6 @@ export interface FilterChipProps {
|
|
|
10
10
|
* Whether the chip is currently in an active/selected state.
|
|
11
11
|
*/
|
|
12
12
|
active?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* Callback when the chip is clicked.
|
|
15
|
-
*/
|
|
16
|
-
onClick?: () => void;
|
|
17
13
|
/**
|
|
18
14
|
* Optional icon rendered before the label.
|
|
19
15
|
*/
|
|
@@ -23,10 +19,6 @@ export interface FilterChipProps {
|
|
|
23
19
|
* @default "medium"
|
|
24
20
|
*/
|
|
25
21
|
size?: "small" | "medium";
|
|
26
|
-
/**
|
|
27
|
-
* Additional class names.
|
|
28
|
-
*/
|
|
29
|
-
className?: string;
|
|
30
22
|
/**
|
|
31
23
|
* Whether the chip is disabled.
|
|
32
24
|
*/
|
|
@@ -47,17 +39,19 @@ const sizeClasses = {
|
|
|
47
39
|
*
|
|
48
40
|
* @group Interactive components
|
|
49
41
|
*/
|
|
50
|
-
export function FilterChip({
|
|
42
|
+
export const FilterChip = React.forwardRef<HTMLButtonElement, FilterChipProps>(function FilterChip({
|
|
51
43
|
children,
|
|
52
44
|
active = false,
|
|
53
45
|
onClick,
|
|
54
46
|
icon,
|
|
55
47
|
size = "medium",
|
|
56
48
|
className,
|
|
57
|
-
disabled = false
|
|
58
|
-
|
|
49
|
+
disabled = false,
|
|
50
|
+
...rest
|
|
51
|
+
}: FilterChipProps, ref) {
|
|
59
52
|
return (
|
|
60
53
|
<button
|
|
54
|
+
ref={ref}
|
|
61
55
|
type="button"
|
|
62
56
|
onClick={onClick}
|
|
63
57
|
disabled={disabled}
|
|
@@ -76,9 +70,10 @@ export function FilterChip({
|
|
|
76
70
|
disabled && "opacity-50 cursor-not-allowed",
|
|
77
71
|
className
|
|
78
72
|
)}
|
|
73
|
+
{...rest}
|
|
79
74
|
>
|
|
80
75
|
{icon}
|
|
81
76
|
{children}
|
|
82
77
|
</button>
|
|
83
78
|
);
|
|
84
|
-
}
|
|
79
|
+
});
|
|
@@ -212,9 +212,10 @@ export const VirtualTableHeaderRow = ({
|
|
|
212
212
|
className={cls(defaultBorderMixin, "z-20 sticky min-w-full flex w-fit flex-row top-0 left-0 border-b bg-surface-50 dark:bg-surface-900")}
|
|
213
213
|
style={{ height: headerHeight }}>
|
|
214
214
|
{columns.map((column, columnIndex) => {
|
|
215
|
+
const columnFilter = column && filter && filter[column.key] ? filter[column.key] : undefined;
|
|
215
216
|
const filterForThisProperty: [VirtualTableWhereFilterOp, unknown] | undefined =
|
|
216
|
-
|
|
217
|
-
?
|
|
217
|
+
columnFilter
|
|
218
|
+
? (Array.isArray(columnFilter[0]) ? (columnFilter as [VirtualTableWhereFilterOp, unknown][])[0] : (columnFilter as [VirtualTableWhereFilterOp, unknown]))
|
|
218
219
|
: undefined;
|
|
219
220
|
|
|
220
221
|
const isDraggable = !column.frozen && !!onColumnsOrderChange;
|
|
@@ -274,6 +274,25 @@ export type OnVirtualTableColumnResizeParams = {
|
|
|
274
274
|
column: VirtualTableColumn
|
|
275
275
|
};
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* @see Table
|
|
279
|
+
* @group Components
|
|
280
|
+
*/
|
|
281
|
+
export type WhereFilterOp =
|
|
282
|
+
| "<"
|
|
283
|
+
| "<="
|
|
284
|
+
| "=="
|
|
285
|
+
| "!="
|
|
286
|
+
| ">="
|
|
287
|
+
| ">"
|
|
288
|
+
| "array-contains"
|
|
289
|
+
| "in"
|
|
290
|
+
| "not-in"
|
|
291
|
+
| "array-contains-any";
|
|
292
|
+
|
|
293
|
+
export type FilterValues<Key extends string> =
|
|
294
|
+
Partial<Record<Key, [WhereFilterOp, unknown] | [WhereFilterOp, unknown][]>>;
|
|
295
|
+
|
|
277
296
|
/**
|
|
278
297
|
* @see Table
|
|
279
298
|
* @group Components
|
|
@@ -284,7 +303,7 @@ export type VirtualTableSort = "asc" | "desc" | undefined;
|
|
|
284
303
|
* @see Table
|
|
285
304
|
* @group Components
|
|
286
305
|
*/
|
|
287
|
-
export type VirtualTableFilterValues<Key extends string> =
|
|
306
|
+
export type VirtualTableFilterValues<Key extends string> = FilterValues<Key>;
|
|
288
307
|
|
|
289
308
|
/**
|
|
290
309
|
* Filter conditions in a `Query.where()` clause are specified using the
|
|
@@ -292,14 +311,4 @@ export type VirtualTableFilterValues<Key extends string> = Partial<Record<Key, [
|
|
|
292
311
|
* @see Table
|
|
293
312
|
* @group Models
|
|
294
313
|
*/
|
|
295
|
-
export type VirtualTableWhereFilterOp =
|
|
296
|
-
| "<"
|
|
297
|
-
| "<="
|
|
298
|
-
| "=="
|
|
299
|
-
| "!="
|
|
300
|
-
| ">="
|
|
301
|
-
| ">"
|
|
302
|
-
| "array-contains"
|
|
303
|
-
| "in"
|
|
304
|
-
| "not-in"
|
|
305
|
-
| "array-contains-any";
|
|
314
|
+
export type VirtualTableWhereFilterOp = WhereFilterOp;
|