@vuu-ui/vuu-filters 3.5.1 → 3.5.3

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,5 +1,5 @@
1
1
  {
2
- "version": "3.5.1",
2
+ "version": "3.5.3",
3
3
  "main": "./src/index.js",
4
4
  "author": "heswell",
5
5
  "license": "Apache-2.0",
@@ -10,19 +10,19 @@
10
10
  "type-defs": "node ../../scripts/build-type-defs.mjs"
11
11
  },
12
12
  "devDependencies": {
13
- "@vuu-ui/vuu-data-types": "3.5.1",
14
- "@vuu-ui/vuu-protocol-types": "3.5.1",
15
- "@vuu-ui/vuu-table-types": "3.5.1",
16
- "@vuu-ui/vuu-filter-types": "3.5.1"
13
+ "@vuu-ui/vuu-data-types": "3.5.3",
14
+ "@vuu-ui/vuu-protocol-types": "3.5.3",
15
+ "@vuu-ui/vuu-table-types": "3.5.3",
16
+ "@vuu-ui/vuu-filter-types": "3.5.3"
17
17
  },
18
18
  "dependencies": {
19
- "@vuu-ui/vuu-context-menu": "3.5.1",
20
- "@vuu-ui/vuu-data-react": "3.5.1",
21
- "@vuu-ui/vuu-filter-parser": "3.5.1",
22
- "@vuu-ui/vuu-popups": "3.5.1",
23
- "@vuu-ui/vuu-ui-controls": "3.5.1",
24
- "@vuu-ui/vuu-table": "3.5.1",
25
- "@vuu-ui/vuu-utils": "3.5.1",
19
+ "@vuu-ui/vuu-context-menu": "3.5.3",
20
+ "@vuu-ui/vuu-data-react": "3.5.3",
21
+ "@vuu-ui/vuu-filter-parser": "3.5.3",
22
+ "@vuu-ui/vuu-popups": "3.5.3",
23
+ "@vuu-ui/vuu-ui-controls": "3.5.3",
24
+ "@vuu-ui/vuu-table": "3.5.3",
25
+ "@vuu-ui/vuu-utils": "3.5.3",
26
26
  "@salt-ds/core": "1.54.1",
27
27
  "@salt-ds/lab": "1.0.0-alpha.83",
28
28
  "@salt-ds/styles": "0.2.1",
@@ -43,6 +43,10 @@ const css = `
43
43
  --saltButton-padding: var(--salt-spacing-200);
44
44
  }
45
45
 
46
+ .vuuColumnFilter-toggle {
47
+ max-width: none;
48
+ }
49
+
46
50
  .vuuColumnFilter-invalid {
47
51
  border-color: red;
48
52
  }
@@ -39,7 +39,8 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
39
39
  return /*#__PURE__*/ jsxs(SegmentedButtonGroup, {
40
40
  ...buttonGroupProps,
41
41
  className: clsx(classBase, className, {
42
- [`${classBase}-invalid`]: isInvalid
42
+ [`${classBase}-invalid`]: isInvalid,
43
+ [`${classBase}-toggle`]: "toggle" === variant
43
44
  }),
44
45
  ref: forwardRef,
45
46
  children: [
@@ -3,6 +3,14 @@ const css = `
3
3
  & .vuuToggleFilter-onlyAvailableValue:not([aria-checked="true"], :hover) {
4
4
  background-color: var(--vuuToggleFilter-implicitActive-bg, var(--salt-actionable-bold-background));
5
5
  }
6
+
7
+ & .vuuToggleFilter-unavailableValue:not([aria-checked="true"]) {
8
+ background: var(--toggleButton-background);
9
+ border-color: var(--toggleButton-borderColor);
10
+ color: var(--toggleButton-text-color);
11
+ cursor: var(--vuuToggleFilter-unavailableValue-cursor, var(--salt-cursor-disabled));
12
+ opacity: var(--vuuToggleFilter-unavailableValue-opacity, .4);
13
+ }
6
14
  }
7
15
 
8
16
 
@@ -1,12 +1,15 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { ToggleButton, ToggleButtonGroup } from "@salt-ds/core";
2
+ import { ToggleButton, ToggleButtonGroup, Tooltip } from "@salt-ds/core";
3
3
  import { useComponentCssInjection } from "@salt-ds/styles";
4
4
  import { useWindow } from "@salt-ds/window";
5
5
  import clsx from "clsx";
6
6
  import { forwardRef, useCallback } from "react";
7
7
  import { useToggleFilter } from "./useToggleFilter.js";
8
8
  import ToggleFilter_0 from "./ToggleFilter.css";
9
- const getValues = (defaultValue, value)=>{
9
+ const toToggleButtonValue = (value)=>"boolean" == typeof value ? `${value}` : value;
10
+ const getValues = (defaultValueProp, valueProp)=>{
11
+ const defaultValue = toToggleButtonValue(defaultValueProp);
12
+ const value = toToggleButtonValue(valueProp);
10
13
  if (void 0 === defaultValue && void 0 === value) return [
11
14
  "all"
12
15
  ];
@@ -24,6 +27,7 @@ const getValues = (defaultValue, value)=>{
24
27
  ];
25
28
  };
26
29
  const classBase = "vuuToggleFilter";
30
+ const NO_MATCHING_DATA = "No matching data";
27
31
  const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className, column, defaultValue: defaultValueProp, labels, onCommit, table, value: valueProp, values, ...ToggleButtonGroupProps }, forwardedRef) {
28
32
  const targetWindow = useWindow();
29
33
  useComponentCssInjection({
@@ -33,16 +37,22 @@ const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className,
33
37
  });
34
38
  if (void 0 !== labels && labels.length !== values.length) throw Error("[ToggleFilter] labels must have the same number of items as values");
35
39
  const [defaultValue, value] = getValues(defaultValueProp, valueProp);
36
- const onlyAvailableValue = useToggleFilter({
40
+ const stringValues = values.map(String);
41
+ const { onlyAvailableValue, unavailableValues } = useToggleFilter({
37
42
  column,
38
43
  table,
39
- values
44
+ values: stringValues
40
45
  });
41
46
  const handleChange = useCallback((e)=>{
42
47
  const value = e.currentTarget.value;
43
- "all" === value ? onCommit(e, "") : onCommit(e, value);
48
+ if ("all" === value) onCommit(e, "");
49
+ else {
50
+ const toggleValue = values.find((v)=>`${v}` === value);
51
+ onCommit(e, "boolean" == typeof toggleValue ? toggleValue : value);
52
+ }
44
53
  }, [
45
- onCommit
54
+ onCommit,
55
+ values
46
56
  ]);
47
57
  return /*#__PURE__*/ jsxs(ToggleButtonGroup, {
48
58
  ...ToggleButtonGroupProps,
@@ -54,15 +64,23 @@ const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className,
54
64
  children: [
55
65
  /*#__PURE__*/ jsx(ToggleButton, {
56
66
  value: "all",
57
- children: "ALL"
67
+ children: "All"
58
68
  }, "all"),
59
- values.map((toggleValue, i)=>/*#__PURE__*/ jsx(ToggleButton, {
60
- className: clsx({
61
- [`${classBase}-onlyAvailableValue`]: onlyAvailableValue === toggleValue
62
- }),
63
- value: toggleValue,
64
- children: labels?.[i] ?? toggleValue
65
- }, toggleValue))
69
+ stringValues.map((toggleValue, i)=>{
70
+ const unavailable = unavailableValues.includes(toggleValue);
71
+ return /*#__PURE__*/ jsx(Tooltip, {
72
+ content: NO_MATCHING_DATA,
73
+ disabled: !unavailable,
74
+ children: /*#__PURE__*/ jsx(ToggleButton, {
75
+ className: clsx({
76
+ [`${classBase}-onlyAvailableValue`]: onlyAvailableValue === toggleValue,
77
+ [`${classBase}-unavailableValue`]: unavailable
78
+ }),
79
+ value: toggleValue,
80
+ children: labels?.[i] ?? toggleValue
81
+ })
82
+ }, toggleValue);
83
+ })
66
84
  ]
67
85
  });
68
86
  });
@@ -1,57 +1,81 @@
1
- import { useSavedFilters } from "../filter-provider/FilterContext.js";
2
1
  import { useTypeaheadSuggestions } from "@vuu-ui/vuu-data-react";
3
- import { useCallback, useMemo, useRef, useState } from "react";
4
- import { filtersAreEqual, getVuuTable } from "@vuu-ui/vuu-utils";
2
+ import { parseFilter } from "@vuu-ui/vuu-filter-parser";
3
+ import { extractFilterForColumn, getVuuTable, useDataSource } from "@vuu-ui/vuu-utils";
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
5
  const assertValid = (values, actualValues)=>{
6
6
  if (actualValues.some((val)=>-1 === values.indexOf(val))) console.warn(`[useToggleFilter] ToggleFilter is configured with values which do not include all values from data source
7
7
  [${values.join()}]
8
8
  [${actualValues.join()}]`);
9
9
  };
10
- const typeaheadRefreshRequired = (f1, f2)=>{
11
- if (null === f1 && null === f2) return false;
12
- if (null === f1 || null === f2) return true;
13
- if (filtersAreEqual(f1, f2)) return false;
14
- return true;
10
+ const hasFilterOnColumn = (filterSpec, column)=>{
11
+ if (void 0 === filterSpec || "" === filterSpec.filter) return false;
12
+ const filter = filterSpec.filterStruct ?? parseFilter(filterSpec.filter);
13
+ return void 0 !== extractFilterForColumn(filter, column);
15
14
  };
16
15
  const useToggleFilter = ({ column, table, values })=>{
17
- const { currentFilter } = useSavedFilters();
18
- const currentFilterRef = useRef(currentFilter.filter);
19
- const [typeaheadValues, setTypeaheadValues] = useState([]);
16
+ const dataSource = useDataSource(false);
17
+ const [typeaheadValues, setTypeaheadValues] = useState(void 0);
20
18
  const getSuggestions = useTypeaheadSuggestions();
21
- const refreshSuggestions = useCallback((table)=>{
22
- const vuuTable = getVuuTable(table);
19
+ const requestIdRef = useRef(0);
20
+ const valuesRef = useRef(values);
21
+ valuesRef.current = values;
22
+ const valuesKey = values.join("\u0000");
23
+ const vuuTable = table ? getVuuTable(table) : void 0;
24
+ const tableModule = vuuTable?.module;
25
+ const tableName = vuuTable?.table;
26
+ const checkAvailability = useCallback(()=>{
27
+ if (void 0 === tableModule || void 0 === tableName || void 0 === dataSource) return;
28
+ if (hasFilterOnColumn(dataSource.filter, column)) return;
29
+ const requestId = ++requestIdRef.current;
23
30
  const params = [
24
- vuuTable,
31
+ {
32
+ module: tableModule,
33
+ table: tableName
34
+ },
25
35
  column
26
36
  ];
27
37
  getSuggestions(params).then((suggestions)=>{
28
- if (false === suggestions) setTypeaheadValues([]);
29
- else {
30
- assertValid(values, suggestions);
31
- setTypeaheadValues(suggestions);
32
- }
38
+ if (requestId !== requestIdRef.current || false === suggestions) return;
39
+ assertValid(valuesRef.current, suggestions);
40
+ setTypeaheadValues(suggestions);
33
41
  });
34
42
  }, [
35
43
  column,
44
+ dataSource,
36
45
  getSuggestions,
37
- values
46
+ tableModule,
47
+ tableName
38
48
  ]);
39
- useMemo(()=>{
40
- if (table) refreshSuggestions(table);
49
+ useEffect(()=>{
50
+ checkAvailability();
41
51
  }, [
42
- refreshSuggestions,
43
- table
52
+ checkAvailability
44
53
  ]);
45
- useMemo(()=>{
46
- const { current: filter } = currentFilterRef;
47
- if (table && typeaheadRefreshRequired(filter, currentFilter.filter)) refreshSuggestions(table);
48
- currentFilterRef.current = currentFilter.filter;
54
+ useEffect(()=>{
55
+ if (dataSource) {
56
+ const handleConfigChange = (_config, _range, _confirmed, configChanges)=>{
57
+ if (configChanges?.filterChanged) checkAvailability();
58
+ };
59
+ dataSource.on("config", handleConfigChange);
60
+ return ()=>{
61
+ dataSource.removeListener("config", handleConfigChange);
62
+ };
63
+ }
49
64
  }, [
50
- currentFilter.filter,
51
- refreshSuggestions,
52
- table
65
+ checkAvailability,
66
+ dataSource
67
+ ]);
68
+ return useMemo(()=>{
69
+ if (void 0 === typeaheadValues) return {
70
+ unavailableValues: []
71
+ };
72
+ return {
73
+ onlyAvailableValue: 1 === typeaheadValues.length ? typeaheadValues[0] : void 0,
74
+ unavailableValues: valuesRef.current.filter((value)=>!typeaheadValues.includes(value))
75
+ };
76
+ }, [
77
+ typeaheadValues,
78
+ valuesKey
53
79
  ]);
54
- const [firstValue] = typeaheadValues;
55
- return 1 === typeaheadValues.length ? firstValue : void 0;
56
80
  };
57
81
  export { useToggleFilter };
package/types/index.d.ts CHANGED
@@ -21,4 +21,4 @@ export { FilterNamePrompt as SaveFilterConfirmPrompt, type FilterNamePromptProps
21
21
  export { SavedFilterPanel, type SavedFilterPanelProps, } from "./saved-filters/SavedFilterPanel";
22
22
  export type { FilterClickHandler } from "./saved-filters/useSavedFilterPanel";
23
23
  export { TabbedFilterContainer, type TabbedFilterContainerProps, } from "./tabbed-filter-container/TabbedFilterContainer";
24
- export { ToggleFilter, type ToggleFilterProps, } from "./toggle-filter/ToggleFilter";
24
+ export { ToggleFilter, type ToggleFilterProps, type ToggleFilterValue, } from "./toggle-filter/ToggleFilter";
@@ -1,6 +1,8 @@
1
1
  import { ToggleButtonGroupProps } from "@salt-ds/core";
2
2
  import { CommitHandler } from "@vuu-ui/vuu-utils";
3
3
  import { type ToggleFilterHookProps } from "./useToggleFilter";
4
+ export type ToggleFilterValue = string | number | boolean;
5
+ type Value = ToggleButtonGroupProps["value"];
4
6
  /**
5
7
  * ToggleFilter is designed to work with a FilterProvider and
6
8
  * DataSourceProvider. With a DataSourceProvider, values will
@@ -11,12 +13,20 @@ import { type ToggleFilterHookProps } from "./useToggleFilter";
11
13
  * With neither of these Providers available, the ToggleFilter is
12
14
  * behaving like a regular ToggleButtonGroup.
13
15
  */
14
- export interface ToggleFilterProps extends ToggleButtonGroupProps, ToggleFilterHookProps {
16
+ export interface ToggleFilterProps extends Omit<ToggleButtonGroupProps, "defaultValue" | "value">, Omit<ToggleFilterHookProps, "values"> {
17
+ defaultValue?: ToggleFilterValue | Value;
15
18
  /**
16
19
  * Optional labels for the toggle buttons. If provided, must contain
17
20
  * the same number of items as values. Defaults to values.
18
21
  */
19
22
  labels?: string[];
20
23
  onCommit: CommitHandler<HTMLElement>;
24
+ value?: ToggleFilterValue | Value;
25
+ /**
26
+ * Values for the toggle buttons. Boolean values are converted to
27
+ * strings internally, the original value is passed to onCommit.
28
+ */
29
+ values: ToggleFilterValue[];
21
30
  }
22
31
  export declare const ToggleFilter: import("react").ForwardRefExoticComponent<ToggleFilterProps & import("react").RefAttributes<HTMLDivElement>>;
32
+ export {};
@@ -8,4 +8,22 @@ export interface ToggleFilterHookProps {
8
8
  table?: TableSchemaTable;
9
9
  values: string[];
10
10
  }
11
- export declare const useToggleFilter: ({ column, table, values, }: ToggleFilterHookProps) => string | undefined;
11
+ export interface ToggleFilterHookResult {
12
+ /**
13
+ * If only one of the toggle values has matching data, that value.
14
+ */
15
+ onlyAvailableValue?: string;
16
+ /**
17
+ * Toggle values for which there is no matching data.
18
+ */
19
+ unavailableValues: string[];
20
+ }
21
+ /**
22
+ * Determines which toggle values have matching data, using typeahead
23
+ * against the DataSource. Typeahead respects the filter applied to the
24
+ * DataSource, so while a filter on this column is in effect, results
25
+ * would only reflect the filtered value. In that case no check is made
26
+ * and the result of the last check (if any) is retained. If there has
27
+ * been no check, all values are assumed to possibly have data.
28
+ */
29
+ export declare const useToggleFilter: ({ column, table, values, }: ToggleFilterHookProps) => ToggleFilterHookResult;