@vuu-ui/vuu-filters 3.5.0 → 3.5.2

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.0",
2
+ "version": "3.5.2",
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.0",
14
- "@vuu-ui/vuu-protocol-types": "3.5.0",
15
- "@vuu-ui/vuu-table-types": "3.5.0",
16
- "@vuu-ui/vuu-filter-types": "3.5.0"
13
+ "@vuu-ui/vuu-data-types": "3.5.2",
14
+ "@vuu-ui/vuu-protocol-types": "3.5.2",
15
+ "@vuu-ui/vuu-table-types": "3.5.2",
16
+ "@vuu-ui/vuu-filter-types": "3.5.2"
17
17
  },
18
18
  "dependencies": {
19
- "@vuu-ui/vuu-context-menu": "3.5.0",
20
- "@vuu-ui/vuu-data-react": "3.5.0",
21
- "@vuu-ui/vuu-filter-parser": "3.5.0",
22
- "@vuu-ui/vuu-popups": "3.5.0",
23
- "@vuu-ui/vuu-ui-controls": "3.5.0",
24
- "@vuu-ui/vuu-table": "3.5.0",
25
- "@vuu-ui/vuu-utils": "3.5.0",
19
+ "@vuu-ui/vuu-context-menu": "3.5.2",
20
+ "@vuu-ui/vuu-data-react": "3.5.2",
21
+ "@vuu-ui/vuu-filter-parser": "3.5.2",
22
+ "@vuu-ui/vuu-popups": "3.5.2",
23
+ "@vuu-ui/vuu-ui-controls": "3.5.2",
24
+ "@vuu-ui/vuu-table": "3.5.2",
25
+ "@vuu-ui/vuu-utils": "3.5.2",
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
  }
@@ -9,7 +9,7 @@ import { useWindow } from "@salt-ds/window";
9
9
  import ColumnFilter_0 from "./ColumnFilter.css";
10
10
  import { isBetweenOperator, withDateTimePattern } from "@vuu-ui/vuu-utils";
11
11
  const classBase = "vuuColumnFilter";
12
- const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(function({ InputProps: InputPropsProp, TypeaheadProps, className, column, defaultValue, extendedFilterOptions, onColumnFilterChange, onColumnRangeFilterChange, onCommit: onCommitProp, operator = "=", pattern, table, value: valueProp, values, variant, ...buttonGroupProps }, forwardRef) {
12
+ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(function({ InputProps: InputPropsProp, TypeaheadProps, className, column, defaultValue, extendedFilterOptions, labels, onColumnFilterChange, onColumnRangeFilterChange, onCommit: onCommitProp, operator = "=", pattern, table, value: valueProp, values, variant, ...buttonGroupProps }, forwardRef) {
13
13
  const targetWindow = useWindow();
14
14
  useComponentCssInjection({
15
15
  testId: "vuu-filter-editor",
@@ -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: [
@@ -48,6 +49,7 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
48
49
  TypeaheadProps,
49
50
  commitWhenCleared: true,
50
51
  dataDescriptor: controlDescriptor,
52
+ labels,
51
53
  onCommit,
52
54
  table,
53
55
  values,
@@ -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,5 +1,5 @@
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";
@@ -24,15 +24,17 @@ const getValues = (defaultValue, value)=>{
24
24
  ];
25
25
  };
26
26
  const classBase = "vuuToggleFilter";
27
- const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className, column, defaultValue: defaultValueProp, onCommit, table, value: valueProp, values, ...ToggleButtonGroupProps }, forwardedRef) {
27
+ const NO_MATCHING_DATA = "No matching data";
28
+ const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className, column, defaultValue: defaultValueProp, labels, onCommit, table, value: valueProp, values, ...ToggleButtonGroupProps }, forwardedRef) {
28
29
  const targetWindow = useWindow();
29
30
  useComponentCssInjection({
30
31
  testId: "vuu-toggle-filter",
31
32
  css: ToggleFilter_0,
32
33
  window: targetWindow
33
34
  });
35
+ if (void 0 !== labels && labels.length !== values.length) throw Error("[ToggleFilter] labels must have the same number of items as values");
34
36
  const [defaultValue, value] = getValues(defaultValueProp, valueProp);
35
- const onlyAvailableValue = useToggleFilter({
37
+ const { onlyAvailableValue, unavailableValues } = useToggleFilter({
36
38
  column,
37
39
  table,
38
40
  values
@@ -53,15 +55,23 @@ const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className,
53
55
  children: [
54
56
  /*#__PURE__*/ jsx(ToggleButton, {
55
57
  value: "all",
56
- children: "ALL"
58
+ children: "All"
57
59
  }, "all"),
58
- values.map((toggleValue)=>/*#__PURE__*/ jsx(ToggleButton, {
59
- className: clsx({
60
- [`${classBase}-onlyAvailableValue`]: onlyAvailableValue === toggleValue
61
- }),
62
- value: toggleValue,
63
- children: toggleValue
64
- }, toggleValue))
60
+ values.map((toggleValue, i)=>{
61
+ const unavailable = unavailableValues.includes(toggleValue);
62
+ return /*#__PURE__*/ jsx(Tooltip, {
63
+ content: NO_MATCHING_DATA,
64
+ disabled: !unavailable,
65
+ children: /*#__PURE__*/ jsx(ToggleButton, {
66
+ className: clsx({
67
+ [`${classBase}-onlyAvailableValue`]: onlyAvailableValue === toggleValue,
68
+ [`${classBase}-unavailableValue`]: unavailable
69
+ }),
70
+ value: toggleValue,
71
+ children: labels?.[i] ?? toggleValue
72
+ })
73
+ }, toggleValue);
74
+ })
65
75
  ]
66
76
  });
67
77
  });
@@ -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 };
@@ -2,7 +2,7 @@ import { type SegmentedButtonGroupProps } from "@salt-ds/core";
2
2
  import { DataItemEditControlProps } from "@vuu-ui/vuu-data-react";
3
3
  import { ColumnFilterHookProps } from "./useColumnFilter";
4
4
  import { type DateTimePattern } from "@vuu-ui/vuu-utils";
5
- export interface ColumnFilterProps extends ColumnFilterHookProps, Omit<SegmentedButtonGroupProps, "defaultValue">, Pick<DataItemEditControlProps, "TypeaheadProps" | "table" | "values" | "variant"> {
5
+ export interface ColumnFilterProps extends ColumnFilterHookProps, Omit<SegmentedButtonGroupProps, "defaultValue">, Pick<DataItemEditControlProps, "TypeaheadProps" | "labels" | "table" | "values" | "variant"> {
6
6
  /**
7
7
  * Temporal columns only. The date/time pattern used by the filter control,
8
8
  * overriding any pattern configured on the column type. The pattern also
@@ -12,6 +12,11 @@ import { type ToggleFilterHookProps } from "./useToggleFilter";
12
12
  * behaving like a regular ToggleButtonGroup.
13
13
  */
14
14
  export interface ToggleFilterProps extends ToggleButtonGroupProps, ToggleFilterHookProps {
15
+ /**
16
+ * Optional labels for the toggle buttons. If provided, must contain
17
+ * the same number of items as values. Defaults to values.
18
+ */
19
+ labels?: string[];
15
20
  onCommit: CommitHandler<HTMLElement>;
16
21
  }
17
22
  export declare const ToggleFilter: import("react").ForwardRefExoticComponent<ToggleFilterProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -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;