@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/ui",
3
3
  "type": "module",
4
- "version": "0.2.5",
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",
@@ -8,7 +8,7 @@ type CardProps = {
8
8
  style?: React.CSSProperties;
9
9
  onClick?: (e?: React.MouseEvent) => void;
10
10
  className?: string;
11
- };
11
+ } & React.HTMLAttributes<HTMLDivElement>;
12
12
 
13
13
  const Card = React.forwardRef<HTMLDivElement, CardProps>(({
14
14
  children,
@@ -1,33 +1,74 @@
1
1
  "use client";
2
- import React, { ChangeEvent, useCallback, useDeferredValue, useEffect } from "react";
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 previousEventRef = React.useRef<ChangeEvent<any>>(undefined);
8
- const [internalValue, setInternalValue] = React.useState(props.value);
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
- setInternalValue(props.value);
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
- const emptyInitialValue = !props.value;
18
- if (emptyInitialValue && !deferredValue)
19
- return;
20
- if (deferredValue !== props.value && previousEventRef.current && props.onChange) {
21
- props.onChange(previousEventRef.current);
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
- }, [deferredValue, props.value]);
44
+ }, [props.value, props.onChange, props.name]);
24
45
 
25
46
  const internalOnChange = useCallback((event: ChangeEvent<any>) => {
26
- previousEventRef.current = event;
27
- setInternalValue(event.target.value);
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
- value={internalValue}/>
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
- }: FilterChipProps) {
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
- column && filter && filter[column.key]
217
- ? filter[column.key]
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> = Partial<Record<Key, [VirtualTableWhereFilterOp, unknown]>>;
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;