@vuu-ui/vuu-filters 0.8.72 → 0.8.73

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var filterClauseCss = ".vuuFilterClause {\n --vuuExpandoInput-top: 0;\n --content-height: calc(var(--salt-size-base) - var(--salt-spacing-150));\n --vuuExpandoInput-height: var(--content-height);\n --vuuExpandoCombobox-height: var(--content-height);\n --saltButton-height: 16px;\n --saltButton-width: 16px;\n --salt-focused-outlineStyle: dotted;\n\n align-items: center;\n background: var(--salt-container-primary-background);\n border-color: var(--vuu-color-gray-45);\n border-radius: var(--saltButton-borderRadius);\n border-width: 1px;\n border-style: solid;\n display: flex;\n flex-direction: row;\n gap: var(--salt-spacing-50);\n height: var(--salt-size-base);\n min-width: calc(var(--salt-size-base) * 4);\n padding: 0 var(--salt-spacing-100);\n width: fit-content;\n\n\n .saltComboBox-focused {\n outline: none;\n }\n}\n\n.vuuFilterClause:focus-within {\n border-color: var(--vuu-color-purple-10);\n}\n\n.vuu-density-high .vuuFilterClause {\n padding: 4px 8px;\n gap: 4px;\n --salt-text-lineHeight: 12px;\n --saltInputLegacy-fontSize: 12px;\n --saltInputLegacy-minWidth: 12px;\n}\n\n.vuu-density-high .vuuFilterClause .saltInput {\n padding: 0;\n min-height: 16px;\n height: 16px;\n}\n\n.vuuFilterClause .vuuExpandoCombobox {\n flex-basis: auto;\n flex-shrink: 0;\n flex-grow: 0;\n}\n\n.vuuFilterClauseOperator-hidden {\n display: none;\n}\n\n\n.vuuFilterClause .saltInput-focused,\n.vuuFilterClause .saltTokenizedInput-focused {\n outline: none;\n color: var(--salt-content-primary-foreground);\n}\n\n.vuu-theme .saltList {\n --list-borderWidth: 1px;\n --list-borderStyle: solid;\n border-radius: 4px;\n box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.4);\n}\n\n.saltListItem[aria-selected=\"true\"]:not(.saltListItem-checkbox) {\n --list-item-background: var(--list-item-background-active);\n color: var(--list-item-text-color-active);\n}\n\n.saltTokenizedInput {\n height: 16px;\n min-height: 16px;\n}\n\n.saltTokenizedInput .saltInputPill {\n --pill-fontSize: 12px;\n --saltButton-borderStyle: none;\n --pill-background: none;\n height: 16px;\n margin: 0;\n}\n\n.saltTokenizedInput-pillGroup {\n padding: 0;\n height: 16px;\n}\n\n.vuuFilterClause-DatePicker {\n border: none;\n}\n";
3
+ var filterClauseCss = ".vuuFilterClause {\n --vuuExpandoInput-top: 0;\n --content-height: calc(var(--salt-size-base) - var(--salt-spacing-150));\n --vuuExpandoInput-height: var(--content-height);\n --vuuExpandoCombobox-height: var(--content-height);\n --saltButton-height: 16px;\n --saltButton-width: 16px;\n --salt-focused-outlineStyle: dotted;\n\n align-items: center;\n background: var(--salt-container-primary-background);\n border-color: var(--vuu-color-gray-45);\n border-radius: var(--saltButton-borderRadius);\n border-width: 1px;\n border-style: solid;\n display: flex;\n flex-direction: row;\n gap: var(--salt-spacing-50);\n height: var(--salt-size-base);\n min-width: calc(var(--salt-size-base) * 4);\n padding: 0 var(--salt-spacing-100);\n width: fit-content;\n\n .vuuFilterClauseField {\n flex: 0 0 auto;\n }\n .vuuFilterClauseField:last-child {\n flex: 1 0 auto;\n }\n\n .vuuExpandoCombobox {\n flex-basis: auto;\n flex-shrink: 0;\n flex-grow: 0;\n }\n\n &:focus-within {\n border-color: var(--vuu-color-purple-10);\n }\n\n .saltComboBox-focused {\n outline: none;\n }\n\n .saltTokenizedInput {\n height: 16px;\n min-height: 16px;\n }\n\n .saltTokenizedInput .saltInputPill {\n --pill-fontSize: 12px;\n --saltButton-borderStyle: none;\n --pill-background: none;\n height: 16px;\n margin: 0;\n }\n\n .saltTokenizedInput-pillGroup {\n padding: 0;\n height: 16px;\n }\n}\n\n.salt-density-high .vuuFilterClause {\n padding: 4px 8px;\n gap: 4px;\n --salt-text-lineHeight: 12px;\n --saltInputLegacy-fontSize: 12px;\n --saltInputLegacy-minWidth: 12px;\n}\n\n.salt-density-high .vuuFilterClause .saltInput {\n padding: 0;\n min-height: 16px;\n height: 16px;\n}\n\n.vuuFilterClauseOperator-hidden {\n display: none;\n}\n\n.vuuFilterClause .saltInput-focused,\n.vuuFilterClause .saltTokenizedInput-focused {\n outline: none;\n color: var(--salt-content-primary-foreground);\n}\n\n.vuuFilterClause-DatePicker {\n border: none;\n}\n";
4
4
 
5
5
  module.exports = filterClauseCss;
6
6
  //# sourceMappingURL=FilterClause.css.js.map
@@ -32,10 +32,10 @@ const FilterClause = ({
32
32
  // onChangeColumn,
33
33
  onSelectColumn,
34
34
  onSelectOperator,
35
- onFocus,
36
35
  onDeselectValue,
37
36
  operatorRef,
38
- selectedColumn
37
+ selectedColumn,
38
+ valueRef
39
39
  } = useFilterClause.useFilterClause({
40
40
  filterClauseModel,
41
41
  onCancel,
@@ -49,57 +49,49 @@ const FilterClause = ({
49
49
  window: targetWindow
50
50
  });
51
51
  const columns = react.useMemo(() => Object.values(columnsByName), [columnsByName]);
52
- return /* @__PURE__ */ jsxRuntime.jsxs(
53
- "div",
54
- {
55
- className: cx(classBase, className),
56
- ...htmlAttributes,
57
- onFocus,
58
- tabIndex: 0,
59
- children: [
60
- /* @__PURE__ */ jsxRuntime.jsx(
61
- ColumnPicker.ColumnPicker,
62
- {
63
- inputProps,
64
- className: cx(`${classBase}Field`, `${classBase}Column`),
65
- columns,
66
- onSelect: onSelectColumn,
67
- ref: columnRef,
68
- value: filterClause.column ?? ""
69
- },
70
- "column-field"
71
- ),
72
- selectedColumn?.name ? /* @__PURE__ */ jsxRuntime.jsx(
73
- OperatorPicker.OperatorPicker,
74
- {
75
- column: selectedColumn,
76
- inputProps,
77
- className: cx(`${classBase}Field`, `${classBase}Operator`, {
78
- [`${classBase}Operator-hidden`]: selectedColumn === null
79
- }),
80
- onSelect: onSelectOperator,
81
- ref: operatorRef,
82
- value: filterClause.op ?? ""
83
- },
84
- "operator-field"
85
- ) : null,
86
- /* @__PURE__ */ jsxRuntime.jsx(
87
- FilterClauseValueEditor.FilterClauseValueEditor,
88
- {
89
- inputProps,
90
- onChangeValue,
91
- onDeselectValue,
92
- operator: filterClause.op,
93
- selectedColumn,
94
- suggestionProvider,
95
- table: tableSchema.table,
96
- value: filterClause?.values ?? filterClause?.value
97
- },
98
- "value-field"
99
- )
100
- ]
101
- }
102
- );
52
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx(classBase, className), ...htmlAttributes, tabIndex: 0, children: [
53
+ /* @__PURE__ */ jsxRuntime.jsx(
54
+ ColumnPicker.ColumnPicker,
55
+ {
56
+ inputProps,
57
+ className: cx(`${classBase}Field`, `${classBase}Column`),
58
+ columns,
59
+ onSelect: onSelectColumn,
60
+ ref: columnRef,
61
+ value: filterClauseModel.column ?? ""
62
+ },
63
+ "column-field"
64
+ ),
65
+ selectedColumn?.name ? /* @__PURE__ */ jsxRuntime.jsx(
66
+ OperatorPicker.OperatorPicker,
67
+ {
68
+ column: selectedColumn,
69
+ inputProps,
70
+ className: cx(`${classBase}Field`, `${classBase}Operator`, {
71
+ [`${classBase}Operator-hidden`]: selectedColumn === null
72
+ }),
73
+ onSelect: onSelectOperator,
74
+ ref: operatorRef,
75
+ value: filterClauseModel.op ?? ""
76
+ },
77
+ "operator-field"
78
+ ) : null,
79
+ filterClauseModel.op ? /* @__PURE__ */ jsxRuntime.jsx(
80
+ FilterClauseValueEditor.FilterClauseValueEditor,
81
+ {
82
+ inputProps,
83
+ onChangeValue,
84
+ onDeselectValue,
85
+ operator: filterClauseModel.op,
86
+ ref: valueRef,
87
+ selectedColumn,
88
+ suggestionProvider,
89
+ table: tableSchema.table,
90
+ value: filterClause?.values ?? filterClause?.value
91
+ },
92
+ "value-field"
93
+ ) : null
94
+ ] });
103
95
  };
104
96
 
105
97
  exports.FilterClause = FilterClause;
@@ -1 +1 @@
1
- {"version":3,"file":"FilterClause.js","sources":["../../src/filter-clause/FilterClause.tsx"],"sourcesContent":["import type { SuggestionProvider, TableSchema } from \"@vuu-ui/vuu-data-types\";\nimport {\n ColumnDescriptorsByName,\n MultiValueFilterClause,\n SingleValueFilterClause,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { CloseReason } from \"@vuu-ui/vuu-ui-controls\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport cx from \"clsx\";\nimport { HTMLAttributes, useMemo } from \"react\";\nimport { FilterClauseModel } from \"../FilterModel\";\nimport { useFilterClause } from \"./useFilterClause\";\nimport { FilterClauseValueEditor } from \"./value-editors/FilterClauseValueEditor\";\nimport { ColumnPicker } from \"./ColumnPicker\";\n\nimport filterClauseCss from \"./FilterClause.css\";\nimport { OperatorPicker } from \"./OperatorPicker\";\n\nexport type FilterClauseCancelType = \"Backspace\" | \"Escape\";\nexport type FilterClauseCancelHandler = (\n filterClause: FilterClauseModel,\n reason: FilterClauseCancelType\n) => void;\n\nexport interface FilterClauseProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n columnsByName: ColumnDescriptorsByName;\n filterClauseModel: FilterClauseModel;\n onCancel?: FilterClauseCancelHandler;\n onDropdownClose?: (closeReason: CloseReason) => void;\n onDropdownOpen?: () => void;\n onFocusSave?: () => void;\n suggestionProvider?: SuggestionProvider;\n tableSchema: TableSchema;\n}\n\nconst classBase = \"vuuFilterClause\";\n\nexport const FilterClause = ({\n className,\n columnsByName,\n onCancel,\n onDropdownClose,\n onDropdownOpen,\n onFocusSave,\n filterClauseModel,\n suggestionProvider,\n tableSchema,\n ...htmlAttributes\n}: FilterClauseProps) => {\n const {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue,\n // onChangeColumn,\n onSelectColumn,\n onSelectOperator,\n onFocus,\n onDeselectValue,\n operatorRef,\n selectedColumn,\n } = useFilterClause({\n filterClauseModel,\n onCancel,\n onFocusSave,\n columnsByName,\n });\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-filter-clause\",\n css: filterClauseCss,\n window: targetWindow,\n });\n\n const columns = useMemo(() => Object.values(columnsByName), [columnsByName]);\n\n return (\n <div\n className={cx(classBase, className)}\n {...htmlAttributes}\n onFocus={onFocus}\n tabIndex={0}\n >\n <ColumnPicker\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Column`)}\n columns={columns}\n key=\"column-field\"\n onSelect={onSelectColumn}\n ref={columnRef}\n value={filterClause.column ?? \"\"}\n />\n {selectedColumn?.name ? (\n <OperatorPicker\n column={selectedColumn}\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Operator`, {\n [`${classBase}Operator-hidden`]: selectedColumn === null,\n })}\n key=\"operator-field\"\n onSelect={onSelectOperator}\n ref={operatorRef}\n value={filterClause.op ?? \"\"}\n />\n ) : null}\n <FilterClauseValueEditor\n inputProps={inputProps}\n key=\"value-field\"\n onChangeValue={onChangeValue}\n onDeselectValue={onDeselectValue}\n operator={filterClause.op}\n selectedColumn={selectedColumn}\n suggestionProvider={suggestionProvider}\n table={tableSchema.table}\n value={\n (filterClause as MultiValueFilterClause)?.values ??\n (filterClause as SingleValueFilterClause)?.value\n }\n />\n </div>\n );\n};\n"],"names":["useFilterClause","useWindow","useComponentCssInjection","filterClauseCss","useMemo","jsxs","jsx","ColumnPicker","OperatorPicker","FilterClauseValueEditor"],"mappings":";;;;;;;;;;;;;AAqCA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAEX,MAAM,eAAe,CAAC;AAAA,EAC3B,SAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,iBAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,GAAG,cAAA;AACL,CAAyB,KAAA;AACvB,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA;AAAA,IAEA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,MACEA,+BAAgB,CAAA;AAAA,IAClB,iBAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,GACD,CAAA,CAAA;AAED,EAAA,MAAM,eAAeC,gBAAU,EAAA,CAAA;AAC/B,EAAyBC,+BAAA,CAAA;AAAA,IACvB,MAAQ,EAAA,mBAAA;AAAA,IACR,GAAK,EAAAC,cAAA;AAAA,IACL,MAAQ,EAAA,YAAA;AAAA,GACT,CAAA,CAAA;AAED,EAAM,MAAA,OAAA,GAAUC,cAAQ,MAAM,MAAA,CAAO,OAAO,aAAa,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA,CAAA;AAE3E,EACE,uBAAAC,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAG,CAAA,SAAA,EAAW,SAAS,CAAA;AAAA,MACjC,GAAG,cAAA;AAAA,MACJ,OAAA;AAAA,MACA,QAAU,EAAA,CAAA;AAAA,MAEV,QAAA,EAAA;AAAA,wBAAAC,cAAA;AAAA,UAACC,yBAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAQ,MAAA,CAAA,CAAA;AAAA,YACvD,OAAA;AAAA,YAEA,QAAU,EAAA,cAAA;AAAA,YACV,GAAK,EAAA,SAAA;AAAA,YACL,KAAA,EAAO,aAAa,MAAU,IAAA,EAAA;AAAA,WAAA;AAAA,UAH1B,cAAA;AAAA,SAIN;AAAA,QACC,gBAAgB,IACf,mBAAAD,cAAA;AAAA,UAACE,6BAAA;AAAA,UAAA;AAAA,YACC,MAAQ,EAAA,cAAA;AAAA,YACR,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAY,QAAA,CAAA,EAAA;AAAA,cACzD,CAAC,CAAA,EAAG,SAAS,CAAA,eAAA,CAAiB,GAAG,cAAmB,KAAA,IAAA;AAAA,aACrD,CAAA;AAAA,YAED,QAAU,EAAA,gBAAA;AAAA,YACV,GAAK,EAAA,WAAA;AAAA,YACL,KAAA,EAAO,aAAa,EAAM,IAAA,EAAA;AAAA,WAAA;AAAA,UAHtB,gBAAA;AAAA,SAKJ,GAAA,IAAA;AAAA,wBACJF,cAAA;AAAA,UAACG,+CAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YAEA,aAAA;AAAA,YACA,eAAA;AAAA,YACA,UAAU,YAAa,CAAA,EAAA;AAAA,YACvB,cAAA;AAAA,YACA,kBAAA;AAAA,YACA,OAAO,WAAY,CAAA,KAAA;AAAA,YACnB,KAAA,EACG,YAAyC,EAAA,MAAA,IACzC,YAA0C,EAAA,KAAA;AAAA,WAAA;AAAA,UATzC,aAAA;AAAA,SAWN;AAAA,OAAA;AAAA,KAAA;AAAA,GACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"FilterClause.js","sources":["../../src/filter-clause/FilterClause.tsx"],"sourcesContent":["import type { SuggestionProvider, TableSchema } from \"@vuu-ui/vuu-data-types\";\nimport {\n ColumnDescriptorsByName,\n MultiValueFilterClause,\n SingleValueFilterClause,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { CloseReason } from \"@vuu-ui/vuu-ui-controls\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport cx from \"clsx\";\nimport { HTMLAttributes, useMemo } from \"react\";\nimport { FilterClauseModel } from \"../FilterModel\";\nimport { useFilterClause } from \"./useFilterClause\";\nimport { FilterClauseValueEditor } from \"./value-editors/FilterClauseValueEditor\";\nimport { ColumnPicker } from \"./ColumnPicker\";\n\nimport filterClauseCss from \"./FilterClause.css\";\nimport { OperatorPicker } from \"./OperatorPicker\";\n\nexport type FilterClauseCancelType = \"Backspace\" | \"Escape\";\nexport type FilterClauseCancelHandler = (\n filterClause: FilterClauseModel,\n reason: FilterClauseCancelType\n) => void;\n\nexport interface FilterClauseProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n columnsByName: ColumnDescriptorsByName;\n filterClauseModel: FilterClauseModel;\n onCancel?: FilterClauseCancelHandler;\n onDropdownClose?: (closeReason: CloseReason) => void;\n onDropdownOpen?: () => void;\n onFocusSave?: () => void;\n suggestionProvider?: SuggestionProvider;\n tableSchema: TableSchema;\n}\n\nconst classBase = \"vuuFilterClause\";\n\nexport const FilterClause = ({\n className,\n columnsByName,\n onCancel,\n onDropdownClose,\n onDropdownOpen,\n onFocusSave,\n filterClauseModel,\n suggestionProvider,\n tableSchema,\n ...htmlAttributes\n}: FilterClauseProps) => {\n const {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue,\n // onChangeColumn,\n onSelectColumn,\n onSelectOperator,\n onDeselectValue,\n operatorRef,\n selectedColumn,\n valueRef,\n } = useFilterClause({\n filterClauseModel,\n onCancel,\n onFocusSave,\n columnsByName,\n });\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-filter-clause\",\n css: filterClauseCss,\n window: targetWindow,\n });\n\n const columns = useMemo(() => Object.values(columnsByName), [columnsByName]);\n\n return (\n <div className={cx(classBase, className)} {...htmlAttributes} tabIndex={0}>\n <ColumnPicker\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Column`)}\n columns={columns}\n key=\"column-field\"\n onSelect={onSelectColumn}\n ref={columnRef}\n value={filterClauseModel.column ?? \"\"}\n />\n {selectedColumn?.name ? (\n <OperatorPicker\n column={selectedColumn}\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Operator`, {\n [`${classBase}Operator-hidden`]: selectedColumn === null,\n })}\n key=\"operator-field\"\n onSelect={onSelectOperator}\n ref={operatorRef}\n value={filterClauseModel.op ?? \"\"}\n />\n ) : null}\n {filterClauseModel.op ? (\n <FilterClauseValueEditor\n inputProps={inputProps}\n key=\"value-field\"\n onChangeValue={onChangeValue}\n onDeselectValue={onDeselectValue}\n operator={filterClauseModel.op}\n ref={valueRef}\n selectedColumn={selectedColumn}\n suggestionProvider={suggestionProvider}\n table={tableSchema.table}\n value={\n (filterClause as MultiValueFilterClause)?.values ??\n (filterClause as SingleValueFilterClause)?.value\n }\n />\n ) : null}\n </div>\n );\n};\n"],"names":["useFilterClause","useWindow","useComponentCssInjection","filterClauseCss","useMemo","jsxs","jsx","ColumnPicker","OperatorPicker","FilterClauseValueEditor"],"mappings":";;;;;;;;;;;;;AAqCA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAEX,MAAM,eAAe,CAAC;AAAA,EAC3B,SAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,iBAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,GAAG,cAAA;AACL,CAAyB,KAAA;AACvB,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA;AAAA,IAEA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA,QAAA;AAAA,MACEA,+BAAgB,CAAA;AAAA,IAClB,iBAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,GACD,CAAA,CAAA;AAED,EAAA,MAAM,eAAeC,gBAAU,EAAA,CAAA;AAC/B,EAAyBC,+BAAA,CAAA;AAAA,IACvB,MAAQ,EAAA,mBAAA;AAAA,IACR,GAAK,EAAAC,cAAA;AAAA,IACL,MAAQ,EAAA,YAAA;AAAA,GACT,CAAA,CAAA;AAED,EAAM,MAAA,OAAA,GAAUC,cAAQ,MAAM,MAAA,CAAO,OAAO,aAAa,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA,CAAA;AAE3E,EACE,uBAAAC,eAAA,CAAC,KAAI,EAAA,EAAA,SAAA,EAAW,EAAG,CAAA,SAAA,EAAW,SAAS,CAAI,EAAA,GAAG,cAAgB,EAAA,QAAA,EAAU,CACtE,EAAA,QAAA,EAAA;AAAA,oBAAAC,cAAA;AAAA,MAACC,yBAAA;AAAA,MAAA;AAAA,QACC,UAAA;AAAA,QACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAQ,MAAA,CAAA,CAAA;AAAA,QACvD,OAAA;AAAA,QAEA,QAAU,EAAA,cAAA;AAAA,QACV,GAAK,EAAA,SAAA;AAAA,QACL,KAAA,EAAO,kBAAkB,MAAU,IAAA,EAAA;AAAA,OAAA;AAAA,MAH/B,cAAA;AAAA,KAIN;AAAA,IACC,gBAAgB,IACf,mBAAAD,cAAA;AAAA,MAACE,6BAAA;AAAA,MAAA;AAAA,QACC,MAAQ,EAAA,cAAA;AAAA,QACR,UAAA;AAAA,QACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAY,QAAA,CAAA,EAAA;AAAA,UACzD,CAAC,CAAA,EAAG,SAAS,CAAA,eAAA,CAAiB,GAAG,cAAmB,KAAA,IAAA;AAAA,SACrD,CAAA;AAAA,QAED,QAAU,EAAA,gBAAA;AAAA,QACV,GAAK,EAAA,WAAA;AAAA,QACL,KAAA,EAAO,kBAAkB,EAAM,IAAA,EAAA;AAAA,OAAA;AAAA,MAH3B,gBAAA;AAAA,KAKJ,GAAA,IAAA;AAAA,IACH,kBAAkB,EACjB,mBAAAF,cAAA;AAAA,MAACG,+CAAA;AAAA,MAAA;AAAA,QACC,UAAA;AAAA,QAEA,aAAA;AAAA,QACA,eAAA;AAAA,QACA,UAAU,iBAAkB,CAAA,EAAA;AAAA,QAC5B,GAAK,EAAA,QAAA;AAAA,QACL,cAAA;AAAA,QACA,kBAAA;AAAA,QACA,OAAO,WAAY,CAAA,KAAA;AAAA,QACnB,KAAA,EACG,YAAyC,EAAA,MAAA,IACzC,YAA0C,EAAA,KAAA;AAAA,OAAA;AAAA,MAVzC,aAAA;AAAA,KAaJ,GAAA,IAAA;AAAA,GACN,EAAA,CAAA,CAAA;AAEJ;;;;"}
@@ -190,7 +190,6 @@ const focusFirstClauseIfAllClausesValid = (filterEditor) => {
190
190
  exports.clauseIsNotFirst = clauseIsNotFirst;
191
191
  exports.elementIsFilterClause = elementIsFilterClause;
192
192
  exports.elementIsFilterCombinator = elementIsFilterCombinator;
193
- exports.focusField = focusField;
194
193
  exports.focusFilterClauseField = focusFilterClauseField;
195
194
  exports.focusFirstClauseIfAllClausesValid = focusFirstClauseIfAllClausesValid;
196
195
  exports.focusLastClauseValue = focusLastClauseValue;
@@ -1 +1 @@
1
- {"version":3,"file":"filterClauseFocusManagement.js","sources":["../../src/filter-clause/filterClauseFocusManagement.ts"],"sourcesContent":["import { getElementDataIndex, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { KeyboardEvent } from \"react\";\n\nconst getFilterClauseElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClause\") as HTMLElement;\n\nconst getFilterClauseFieldElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClauseField\") as HTMLElement;\n\ntype FilterClauseFieldName = \"column\" | \"operator\" | \"value\";\nconst mapFilterFieldToClassName: Record<FilterClauseFieldName, string> = {\n column: \"vuuFilterClauseColumn\",\n operator: \"vuuFilterClauseOperator\",\n value: \"vuuFilterClauseValue\",\n};\n\nconst getFilterClauseDetails = ({ classList }: HTMLElement) => {\n if (classList.contains(\"vuuFilterClauseColumn\")) {\n return \"column\";\n } else if (classList.contains(\"vuuFilterClauseOperator\")) {\n return \"operator\";\n } else if (classList.contains(\"vuuFilterClauseValue\")) {\n return \"value\";\n } else {\n throw Error(\n \"getFilterClauseField, filterClauseElemnent is missing required class name\"\n );\n }\n};\n\nexport const getFocusedFieldDetails = (): [number, string] | [] => {\n const el = document.activeElement as HTMLElement;\n const field = queryClosest(el, \".vuuFilterClauseField\");\n const filterClause = queryClosest(field, \".vuuFilterClause\");\n if (filterClause && field) {\n return [getElementDataIndex(filterClause), getFilterClauseDetails(field)];\n } else {\n return [];\n }\n};\n\n// Focus the input control within field. If clause passed, will\n// focus first field within clause\nexport const focusField = (fieldOrClause: HTMLElement | null) => {\n const input = fieldOrClause?.querySelector(\"input\");\n if (input) {\n input.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n }\n};\n\nexport const elementIsFilterCombinator = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClauseCombinator\");\n\nexport const elementIsFilterClause = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClause\");\n\nexport const focusFilterClauseField = (\n filterEditor: HTMLElement,\n filterClauseIndex: number,\n fieldName: FilterClauseFieldName = \"value\"\n) => {\n if (filterEditor) {\n const fieldClassName = mapFilterFieldToClassName[fieldName];\n const field = filterEditor.querySelector(\n `.vuuFilterClause[data-index=\"${filterClauseIndex}\"] .${fieldClassName}`\n ) as HTMLElement;\n focusField(field);\n }\n};\n\nexport const focusLastClauseValue = (filterEditor: HTMLElement) => {\n console.log(\"focusLastClauseValue\");\n const field = Array.from(\n filterEditor?.querySelectorAll(\".vuuFilterClauseField\")\n ).at(-1) as HTMLElement;\n focusField(field);\n};\n\nexport const focusNextFocusableElement = (direction: \"fwd\" | \"bwd\" = \"fwd\") => {\n const activeField = getFocusedField() as HTMLElement;\n const filterClause = getFilterClauseElement(activeField);\n if (direction === \"fwd\" && filterClause?.lastChild === activeField) {\n requestAnimationFrame(() => {\n focusNextFocusableElement();\n });\n } else {\n const nextField =\n direction === \"fwd\"\n ? (activeField?.nextElementSibling as HTMLElement)\n : (activeField?.previousElementSibling as HTMLElement);\n\n nextField?.querySelector(\"input\")?.focus();\n }\n};\n\nconst getFocusedField = () => {\n const activeElement = document.activeElement as HTMLElement;\n if (activeElement?.classList.contains(\"vuuFilterClause-clearButton\")) {\n return activeElement as HTMLElement;\n } else {\n return getFilterClauseFieldElement(activeElement);\n }\n};\n\nexport const focusNextElement = () => {\n const filterClauseField = getFocusedField();\n const filterClause = getFilterClauseElement(filterClauseField);\n if (filterClause && filterClauseField) {\n if (filterClauseField.classList.contains(\"vuuFilterClauseValue\")) {\n const clearButton = filterClause.querySelector(\n \".vuuFilterClause-clearButton\"\n ) as HTMLButtonElement;\n clearButton?.focus();\n } else {\n focusNextFocusableElement();\n }\n }\n};\n\nconst cursorAtTextStart = (input: HTMLInputElement) =>\n input.selectionStart === 0;\n\nconst cursorAtTextEnd = (input: HTMLInputElement) =>\n input.selectionStart === input.value.length;\n\nconst getFieldName = (field: HTMLElement) =>\n field?.classList.contains(\"vuuFilterClauseColumn\")\n ? \"column\"\n : field?.classList.contains(\"vuuFilterClauseOperator\")\n ? \"operator\"\n : \"value\";\n\nexport const clauseIsNotFirst = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n if (clause) {\n const index = getElementDataIndex(clause);\n return index > 0;\n }\n};\n\nconst clauseIsNotLast = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n const nextClause = clause?.nextElementSibling as HTMLElement;\n return nextClause?.classList.contains(\"vuuFilterClauseCombinator\");\n};\n\nexport const tabToPreviousFilterCombinator = (currentElement: HTMLElement) => {\n const filterClause = getFilterClauseElement(currentElement);\n const nextItem = filterClause.previousSibling as HTMLElement;\n console.log(`tab to previous combinator`);\n nextItem?.focus();\n};\n\nexport const navigateToNextFilterClause = (\n currentElement: HTMLElement,\n direction: \"bwd\" | \"fwd\" = \"fwd\"\n) => {\n if (direction === \"bwd\") {\n if (elementIsFilterCombinator(currentElement)) {\n const nextClause = currentElement.previousElementSibling;\n if (elementIsFilterClause(nextClause)) {\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n console.log(`focus field Value ${nextField?.classList}`);\n focusField(nextField);\n }\n } else {\n const filterClause = getFilterClauseElement(currentElement);\n const nextClause = filterClause.previousSibling as HTMLElement;\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n focusField(nextField);\n }\n } else {\n const nextClause = currentElement.nextSibling as HTMLElement;\n focusField(nextClause);\n }\n};\n\n// The logic around preventDefault/stopPropagation is important\n// in this function\nexport const navigateToNextItemIfAtBoundary = (\n evt: KeyboardEvent<HTMLInputElement>\n) => {\n const input = evt.target as HTMLInputElement;\n const field = getFilterClauseFieldElement(input);\n if (evt.key === \"ArrowLeft\") {\n if (cursorAtTextStart(input)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"column\") {\n if (clauseIsNotFirst(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.previousElementSibling as HTMLElement;\n combinator?.focus();\n }\n } else {\n evt.preventDefault();\n focusField(field.previousSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at start. We want the arrowLeft to move the cursor\n evt.stopPropagation();\n } else if (evt.key === \"ArrowRight\") {\n if (cursorAtTextEnd(input as HTMLInputElement)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"value\") {\n if (clauseIsNotLast(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.nextElementSibling as HTMLElement;\n combinator?.focus();\n }\n // Do not preventDefault, stopPropagation\n return;\n } else {\n evt.preventDefault();\n focusField(field.nextSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at end. We want the arrowRight to move the cursor\n evt.stopPropagation();\n }\n};\n\nexport const focusFirstClauseIfAllClausesValid = (\n filterEditor: HTMLElement\n) => {\n const columInput = Array.from(\n filterEditor.querySelectorAll(\".vuuFilterClauseColumn input\")\n ) as HTMLInputElement[];\n if (columInput.every((input) => input.value.length > 0)) {\n setTimeout(() => {\n const input = columInput.at(0);\n input?.select();\n input?.focus();\n }, 100);\n }\n};\n"],"names":["queryClosest","getElementDataIndex"],"mappings":"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"}
1
+ {"version":3,"file":"filterClauseFocusManagement.js","sources":["../../src/filter-clause/filterClauseFocusManagement.ts"],"sourcesContent":["import { getElementDataIndex, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { KeyboardEvent } from \"react\";\n\nconst getFilterClauseElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClause\") as HTMLElement;\n\nconst getFilterClauseFieldElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClauseField\") as HTMLElement;\n\ntype FilterClauseFieldName = \"column\" | \"operator\" | \"value\";\nconst mapFilterFieldToClassName: Record<FilterClauseFieldName, string> = {\n column: \"vuuFilterClauseColumn\",\n operator: \"vuuFilterClauseOperator\",\n value: \"vuuFilterClauseValue\",\n};\n\nconst getFilterClauseDetails = ({ classList }: HTMLElement) => {\n if (classList.contains(\"vuuFilterClauseColumn\")) {\n return \"column\";\n } else if (classList.contains(\"vuuFilterClauseOperator\")) {\n return \"operator\";\n } else if (classList.contains(\"vuuFilterClauseValue\")) {\n return \"value\";\n } else {\n throw Error(\n \"getFilterClauseField, filterClauseElemnent is missing required class name\"\n );\n }\n};\n\nexport const getFocusedFieldDetails = (): [number, string] | [] => {\n const el = document.activeElement as HTMLElement;\n const field = queryClosest(el, \".vuuFilterClauseField\");\n const filterClause = queryClosest(field, \".vuuFilterClause\");\n if (filterClause && field) {\n return [getElementDataIndex(filterClause), getFilterClauseDetails(field)];\n } else {\n return [];\n }\n};\n\n// Focus the input control within field. If clause passed, will\n// focus first field within clause\nconst focusField = (fieldOrClause: HTMLElement | null) => {\n const input = fieldOrClause?.querySelector(\"input\");\n if (input) {\n input.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n }\n};\n\nexport const elementIsFilterCombinator = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClauseCombinator\");\n\nexport const elementIsFilterClause = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClause\");\n\nexport const focusFilterClauseField = (\n filterEditor: HTMLElement,\n filterClauseIndex: number,\n fieldName: FilterClauseFieldName = \"value\"\n) => {\n if (filterEditor) {\n const fieldClassName = mapFilterFieldToClassName[fieldName];\n const field = filterEditor.querySelector(\n `.vuuFilterClause[data-index=\"${filterClauseIndex}\"] .${fieldClassName}`\n ) as HTMLElement;\n focusField(field);\n }\n};\n\nexport const focusLastClauseValue = (filterEditor: HTMLElement) => {\n console.log(\"focusLastClauseValue\");\n const field = Array.from(\n filterEditor?.querySelectorAll(\".vuuFilterClauseField\")\n ).at(-1) as HTMLElement;\n focusField(field);\n};\n\nexport const focusNextFocusableElement = (direction: \"fwd\" | \"bwd\" = \"fwd\") => {\n const activeField = getFocusedField() as HTMLElement;\n const filterClause = getFilterClauseElement(activeField);\n if (direction === \"fwd\" && filterClause?.lastChild === activeField) {\n requestAnimationFrame(() => {\n focusNextFocusableElement();\n });\n } else {\n const nextField =\n direction === \"fwd\"\n ? (activeField?.nextElementSibling as HTMLElement)\n : (activeField?.previousElementSibling as HTMLElement);\n\n nextField?.querySelector(\"input\")?.focus();\n }\n};\n\nconst getFocusedField = () => {\n const activeElement = document.activeElement as HTMLElement;\n if (activeElement?.classList.contains(\"vuuFilterClause-clearButton\")) {\n return activeElement as HTMLElement;\n } else {\n return getFilterClauseFieldElement(activeElement);\n }\n};\n\nexport const focusNextElement = () => {\n const filterClauseField = getFocusedField();\n const filterClause = getFilterClauseElement(filterClauseField);\n if (filterClause && filterClauseField) {\n if (filterClauseField.classList.contains(\"vuuFilterClauseValue\")) {\n const clearButton = filterClause.querySelector(\n \".vuuFilterClause-clearButton\"\n ) as HTMLButtonElement;\n clearButton?.focus();\n } else {\n focusNextFocusableElement();\n }\n }\n};\n\nconst cursorAtTextStart = (input: HTMLInputElement) =>\n input.selectionStart === 0;\n\nconst cursorAtTextEnd = (input: HTMLInputElement) =>\n input.selectionStart === input.value.length;\n\nconst getFieldName = (field: HTMLElement) =>\n field?.classList.contains(\"vuuFilterClauseColumn\")\n ? \"column\"\n : field?.classList.contains(\"vuuFilterClauseOperator\")\n ? \"operator\"\n : \"value\";\n\nexport const clauseIsNotFirst = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n if (clause) {\n const index = getElementDataIndex(clause);\n return index > 0;\n }\n};\n\nconst clauseIsNotLast = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n const nextClause = clause?.nextElementSibling as HTMLElement;\n return nextClause?.classList.contains(\"vuuFilterClauseCombinator\");\n};\n\nexport const tabToPreviousFilterCombinator = (currentElement: HTMLElement) => {\n const filterClause = getFilterClauseElement(currentElement);\n const nextItem = filterClause.previousSibling as HTMLElement;\n console.log(`tab to previous combinator`);\n nextItem?.focus();\n};\n\nexport const navigateToNextFilterClause = (\n currentElement: HTMLElement,\n direction: \"bwd\" | \"fwd\" = \"fwd\"\n) => {\n if (direction === \"bwd\") {\n if (elementIsFilterCombinator(currentElement)) {\n const nextClause = currentElement.previousElementSibling;\n if (elementIsFilterClause(nextClause)) {\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n console.log(`focus field Value ${nextField?.classList}`);\n focusField(nextField);\n }\n } else {\n const filterClause = getFilterClauseElement(currentElement);\n const nextClause = filterClause.previousSibling as HTMLElement;\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n focusField(nextField);\n }\n } else {\n const nextClause = currentElement.nextSibling as HTMLElement;\n focusField(nextClause);\n }\n};\n\n// The logic around preventDefault/stopPropagation is important\n// in this function\nexport const navigateToNextItemIfAtBoundary = (\n evt: KeyboardEvent<HTMLInputElement>\n) => {\n const input = evt.target as HTMLInputElement;\n const field = getFilterClauseFieldElement(input);\n if (evt.key === \"ArrowLeft\") {\n if (cursorAtTextStart(input)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"column\") {\n if (clauseIsNotFirst(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.previousElementSibling as HTMLElement;\n combinator?.focus();\n }\n } else {\n evt.preventDefault();\n focusField(field.previousSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at start. We want the arrowLeft to move the cursor\n evt.stopPropagation();\n } else if (evt.key === \"ArrowRight\") {\n if (cursorAtTextEnd(input as HTMLInputElement)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"value\") {\n if (clauseIsNotLast(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.nextElementSibling as HTMLElement;\n combinator?.focus();\n }\n // Do not preventDefault, stopPropagation\n return;\n } else {\n evt.preventDefault();\n focusField(field.nextSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at end. We want the arrowRight to move the cursor\n evt.stopPropagation();\n }\n};\n\nexport const focusFirstClauseIfAllClausesValid = (\n filterEditor: HTMLElement\n) => {\n const columInput = Array.from(\n filterEditor.querySelectorAll(\".vuuFilterClauseColumn input\")\n ) as HTMLInputElement[];\n if (columInput.every((input) => input.value.length > 0)) {\n setTimeout(() => {\n const input = columInput.at(0);\n input?.select();\n input?.focus();\n }, 100);\n }\n};\n"],"names":["queryClosest","getElementDataIndex"],"mappings":"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"}
@@ -20,6 +20,16 @@ const useFilterClause = ({
20
20
  }, [filterClauseModel]);
21
21
  const columnRef = react.useRef(null);
22
22
  const operatorRef = react.useRef(null);
23
+ const valueRef = react.useRef(null);
24
+ const setValueRef = react.useCallback(
25
+ (el) => {
26
+ valueRef.current = el;
27
+ if (!filterClauseModel.isValid) {
28
+ el?.querySelector("input")?.focus();
29
+ }
30
+ },
31
+ [filterClauseModel.isValid]
32
+ );
23
33
  const removeAndNavigateToNextInputIfAtBoundary = react.useCallback(
24
34
  (evt) => {
25
35
  const input = evt.target;
@@ -109,11 +119,6 @@ const useFilterClause = ({
109
119
  removeAndNavigateToNextInputIfAtBoundary
110
120
  ]
111
121
  );
112
- const handleFocus = react.useCallback((evt) => {
113
- if (filterClauseFocusManagement.elementIsFilterClause(evt.target)) {
114
- filterClauseFocusManagement.focusField(evt.target);
115
- }
116
- }, []);
117
122
  const inputProps = react.useMemo(
118
123
  () => ({
119
124
  onKeyDownCapture: handleKeyDownCaptureNavigation,
@@ -122,10 +127,10 @@ const useFilterClause = ({
122
127
  [handleKeyDownCaptureNavigation]
123
128
  );
124
129
  react.useEffect(() => {
125
- if (filterClauseModel.column === void 0) {
130
+ if (!filterClauseModel.isValid) {
131
+ const inputRef = filterClauseModel.column === void 0 ? columnRef : filterClauseModel.op === void 0 ? operatorRef : null;
126
132
  requestAnimationFrame(() => {
127
- const columnInput = columnRef?.current?.querySelector("input");
128
- columnInput?.focus();
133
+ inputRef?.current?.querySelector("input")?.focus();
129
134
  });
130
135
  }
131
136
  }, [filterClauseModel]);
@@ -136,10 +141,10 @@ const useFilterClause = ({
136
141
  onChangeValue: handleChangeValue,
137
142
  onDeselectValue: handleDeselectValue,
138
143
  onSelectColumn,
139
- onFocus: handleFocus,
140
144
  onSelectOperator,
141
145
  operatorRef,
142
- selectedColumn: columnsByName[filterClause.column ?? ""]
146
+ selectedColumn: columnsByName[filterClauseModel.column ?? ""],
147
+ valueRef: setValueRef
143
148
  };
144
149
  };
145
150
 
@@ -1 +1 @@
1
- {"version":3,"file":"useFilterClause.js","sources":["../../src/filter-clause/useFilterClause.ts"],"sourcesContent":["import { FilterClause, FilterClauseOp } from \"@vuu-ui/vuu-filter-types\";\nimport { hasOpenOptionList } from \"@vuu-ui/vuu-utils\";\nimport {\n FocusEventHandler,\n KeyboardEvent,\n SyntheticEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FilterClauseProps } from \"./FilterClause\";\nimport {\n clauseIsNotFirst,\n elementIsFilterClause,\n focusField,\n focusNextElement,\n focusNextFocusableElement,\n navigateToNextItemIfAtBoundary,\n tabToPreviousFilterCombinator,\n} from \"./filterClauseFocusManagement\";\nexport type FilterClauseEditorHookProps = Pick<\n FilterClauseProps,\n \"columnsByName\" | \"filterClauseModel\" | \"onCancel\" | \"onFocusSave\"\n>;\n\nexport type FilterClauseValueChangeHandler = (\n value: string | string[] | number | number[],\n isFinal?: boolean\n) => void;\n\nexport const useFilterClause = ({\n filterClauseModel,\n onCancel,\n columnsByName,\n onFocusSave,\n}: FilterClauseEditorHookProps) => {\n const [filterClause, setFilterClause] = useState<Partial<FilterClause>>(\n filterClauseModel.isValid ? filterClauseModel.asFilter() : {}\n );\n\n useMemo(() => {\n filterClauseModel.on(\"filterClause\", (filterClause) => {\n setFilterClause(filterClause);\n });\n }, [filterClauseModel]);\n\n const columnRef = useRef<HTMLDivElement>(null);\n const operatorRef = useRef<HTMLDivElement>(null);\n\n const removeAndNavigateToNextInputIfAtBoundary = useCallback(\n (evt: KeyboardEvent) => {\n const input = evt.target as HTMLInputElement;\n if (input.value === \"\") {\n const field = input.closest(\"[data-field]\") as HTMLElement;\n switch (field?.dataset?.field) {\n case \"operator\": {\n filterClauseModel.column = undefined;\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"value\": {\n filterClauseModel.setOp(undefined);\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"column\": {\n if (clauseIsNotFirst(input)) {\n // When we backspace from an empty clause, the clause will be removed.filterClause\n // In this case, we will reposition focus on previous clause, but we\n // don't want the backspace to be effect an edit on that clause.\n evt.preventDefault();\n onCancel?.(filterClauseModel, \"Backspace\");\n }\n }\n }\n }\n },\n [filterClauseModel, onCancel]\n );\n\n const onSelectColumn = (evt: SyntheticEvent, selectedColumn: string) => {\n if (selectedColumn) {\n if (evt?.type === \"keydown\") {\n const { key } = evt as KeyboardEvent;\n if (key === \"Tab\") {\n if (filterClauseModel.column === selectedColumn) {\n // No selection change, allow normal Tab navigation (to Save button)\n return;\n } else {\n // Tab is being used to change selection, keep focus within the clause\n evt.preventDefault();\n }\n }\n }\n }\n filterClauseModel.column = selectedColumn;\n setTimeout(() => {\n console.log(`focus next element`);\n focusNextElement();\n }, 100);\n };\n\n const onSelectOperator = useCallback(\n (_, selectedOp: FilterClauseOp) => {\n filterClauseModel.setOp(selectedOp);\n focusNextElement();\n },\n [filterClauseModel]\n );\n\n const handleChangeValue = useCallback<FilterClauseValueChangeHandler>(\n (value, isFinal) => filterClauseModel.setValue(value, isFinal),\n [filterClauseModel]\n );\n\n const handleDeselectValue = useCallback(\n () => filterClauseModel.setValue(undefined),\n [filterClauseModel]\n );\n\n const handleKeyDownCaptureNavigation = useCallback(\n (evt: KeyboardEvent<HTMLInputElement>) => {\n if ([\"ArrowLeft\", \"ArrowRight\"].includes(evt.key)) {\n navigateToNextItemIfAtBoundary(evt);\n } else if (evt.key === \"Backspace\") {\n removeAndNavigateToNextInputIfAtBoundary(evt);\n } else if (evt.key === \"Escape\") {\n // ignore when optionlist is open, the optionList will be collapsed\n if (!hasOpenOptionList(evt.target)) {\n onCancel?.(filterClauseModel, \"Escape\");\n }\n } else if (evt.key === \"Tab\" && evt.shiftKey) {\n evt.preventDefault();\n tabToPreviousFilterCombinator(evt.target as HTMLElement);\n } else if (evt.key === \"Tab\") {\n // if the clause is valid, skip to save\n if (filterClauseModel.isValid) {\n evt.preventDefault();\n evt.stopPropagation();\n // TODO focus cancel if not changed\n onFocusSave?.();\n }\n }\n },\n [\n filterClauseModel,\n onCancel,\n onFocusSave,\n removeAndNavigateToNextInputIfAtBoundary,\n ]\n );\n\n const handleFocus = useCallback<FocusEventHandler>((evt) => {\n if (elementIsFilterClause(evt.target)) {\n focusField(evt.target);\n }\n }, []);\n\n const inputProps = useMemo(\n () => ({\n onKeyDownCapture: handleKeyDownCaptureNavigation,\n tabIndex: -1,\n }),\n [handleKeyDownCaptureNavigation]\n );\n\n // Do we need this or can we leave it to the filterEditor\n useEffect(() => {\n if (filterClauseModel.column === undefined) {\n requestAnimationFrame(() => {\n const columnInput = columnRef?.current?.querySelector(\"input\");\n columnInput?.focus();\n });\n }\n }, [filterClauseModel]);\n\n return {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue: handleChangeValue,\n onDeselectValue: handleDeselectValue,\n onSelectColumn,\n onFocus: handleFocus,\n onSelectOperator,\n operatorRef,\n selectedColumn: columnsByName[filterClause.column ?? \"\"],\n };\n};\n"],"names":["useState","useMemo","filterClause","useRef","useCallback","focusNextFocusableElement","clauseIsNotFirst","focusNextElement","navigateToNextItemIfAtBoundary","hasOpenOptionList","tabToPreviousFilterCombinator","elementIsFilterClause","focusField","useEffect"],"mappings":"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"}
1
+ {"version":3,"file":"useFilterClause.js","sources":["../../src/filter-clause/useFilterClause.ts"],"sourcesContent":["import { FilterClause, FilterClauseOp } from \"@vuu-ui/vuu-filter-types\";\nimport { hasOpenOptionList } from \"@vuu-ui/vuu-utils\";\nimport {\n KeyboardEvent,\n RefCallback,\n SyntheticEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FilterClauseProps } from \"./FilterClause\";\nimport {\n clauseIsNotFirst,\n focusNextElement,\n focusNextFocusableElement,\n navigateToNextItemIfAtBoundary,\n tabToPreviousFilterCombinator,\n} from \"./filterClauseFocusManagement\";\nexport type FilterClauseEditorHookProps = Pick<\n FilterClauseProps,\n \"columnsByName\" | \"filterClauseModel\" | \"onCancel\" | \"onFocusSave\"\n>;\n\nexport type FilterClauseValueChangeHandler = (\n value: string | string[] | number | number[],\n isFinal?: boolean\n) => void;\n\nexport const useFilterClause = ({\n filterClauseModel,\n onCancel,\n columnsByName,\n onFocusSave,\n}: FilterClauseEditorHookProps) => {\n const [filterClause, setFilterClause] = useState<Partial<FilterClause>>(\n filterClauseModel.isValid ? filterClauseModel.asFilter() : {}\n );\n\n useMemo(() => {\n filterClauseModel.on(\"filterClause\", (filterClause) => {\n setFilterClause(filterClause);\n });\n }, [filterClauseModel]);\n\n const columnRef = useRef<HTMLDivElement>(null);\n const operatorRef = useRef<HTMLDivElement>(null);\n const valueRef = useRef<HTMLDivElement | null>(null);\n\n const setValueRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n valueRef.current = el;\n if (!filterClauseModel.isValid) {\n el?.querySelector(\"input\")?.focus();\n }\n },\n [filterClauseModel.isValid]\n );\n\n const removeAndNavigateToNextInputIfAtBoundary = useCallback(\n (evt: KeyboardEvent) => {\n const input = evt.target as HTMLInputElement;\n if (input.value === \"\") {\n const field = input.closest(\"[data-field]\") as HTMLElement;\n switch (field?.dataset?.field) {\n case \"operator\": {\n filterClauseModel.column = undefined;\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"value\": {\n filterClauseModel.setOp(undefined);\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"column\": {\n if (clauseIsNotFirst(input)) {\n // When we backspace from an empty clause, the clause will be removed.filterClause\n // In this case, we will reposition focus on previous clause, but we\n // don't want the backspace to be effect an edit on that clause.\n evt.preventDefault();\n onCancel?.(filterClauseModel, \"Backspace\");\n }\n }\n }\n }\n },\n [filterClauseModel, onCancel]\n );\n\n const onSelectColumn = (evt: SyntheticEvent, selectedColumn: string) => {\n if (selectedColumn) {\n if (evt?.type === \"keydown\") {\n const { key } = evt as KeyboardEvent;\n if (key === \"Tab\") {\n if (filterClauseModel.column === selectedColumn) {\n // No selection change, allow normal Tab navigation (to Save button)\n return;\n } else {\n // Tab is being used to change selection, keep focus within the clause\n evt.preventDefault();\n }\n }\n }\n }\n filterClauseModel.column = selectedColumn;\n setTimeout(() => {\n console.log(`focus next element`);\n focusNextElement();\n }, 100);\n };\n\n const onSelectOperator = useCallback(\n (_, selectedOp: FilterClauseOp) => {\n filterClauseModel.setOp(selectedOp);\n focusNextElement();\n },\n [filterClauseModel]\n );\n\n const handleChangeValue = useCallback<FilterClauseValueChangeHandler>(\n (value, isFinal) => filterClauseModel.setValue(value, isFinal),\n [filterClauseModel]\n );\n\n const handleDeselectValue = useCallback(\n () => filterClauseModel.setValue(undefined),\n [filterClauseModel]\n );\n\n const handleKeyDownCaptureNavigation = useCallback(\n (evt: KeyboardEvent<HTMLInputElement>) => {\n if ([\"ArrowLeft\", \"ArrowRight\"].includes(evt.key)) {\n navigateToNextItemIfAtBoundary(evt);\n } else if (evt.key === \"Backspace\") {\n removeAndNavigateToNextInputIfAtBoundary(evt);\n } else if (evt.key === \"Escape\") {\n // ignore when optionlist is open, the optionList will be collapsed\n if (!hasOpenOptionList(evt.target)) {\n onCancel?.(filterClauseModel, \"Escape\");\n }\n } else if (evt.key === \"Tab\" && evt.shiftKey) {\n evt.preventDefault();\n tabToPreviousFilterCombinator(evt.target as HTMLElement);\n } else if (evt.key === \"Tab\") {\n // if the clause is valid, skip to save\n if (filterClauseModel.isValid) {\n evt.preventDefault();\n evt.stopPropagation();\n // TODO focus cancel if not changed\n onFocusSave?.();\n }\n }\n },\n [\n filterClauseModel,\n onCancel,\n onFocusSave,\n removeAndNavigateToNextInputIfAtBoundary,\n ]\n );\n\n const inputProps = useMemo(\n () => ({\n onKeyDownCapture: handleKeyDownCaptureNavigation,\n tabIndex: -1,\n }),\n [handleKeyDownCaptureNavigation]\n );\n\n // Do we need this or can we leave it to the filterEditor\n useEffect(() => {\n // leave the valueInput to callbackRef handler above, may\n // fire after the requestAnimationFrame\n if (!filterClauseModel.isValid) {\n const inputRef =\n filterClauseModel.column === undefined\n ? columnRef\n : filterClauseModel.op === undefined\n ? operatorRef\n : null;\n\n requestAnimationFrame(() => {\n inputRef?.current?.querySelector(\"input\")?.focus();\n });\n }\n }, [filterClauseModel]);\n\n return {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue: handleChangeValue,\n onDeselectValue: handleDeselectValue,\n onSelectColumn,\n onSelectOperator,\n operatorRef,\n selectedColumn: columnsByName[filterClauseModel.column ?? \"\"],\n valueRef: setValueRef,\n };\n};\n"],"names":["useState","useMemo","filterClause","useRef","useCallback","focusNextFocusableElement","clauseIsNotFirst","focusNextElement","navigateToNextItemIfAtBoundary","hasOpenOptionList","tabToPreviousFilterCombinator","useEffect"],"mappings":"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"}
@@ -5,72 +5,77 @@ var cx = require('clsx');
5
5
  var FilterClauseValueEditorNumber = require('./FilterClauseValueEditorNumber.js');
6
6
  var FilterClauseValueEditorText = require('./FilterClauseValueEditorText.js');
7
7
  var vuuUtils = require('@vuu-ui/vuu-utils');
8
+ var react = require('react');
8
9
  var FilterClauseValueEditorDate = require('./FilterClauseValueEditorDate.js');
9
10
 
10
11
  const classBase = "vuuFilterClause";
11
- const FilterClauseValueEditor = ({
12
- selectedColumn,
13
- operator,
14
- inputProps,
15
- onChangeValue,
16
- onDeselectValue,
17
- suggestionProvider,
18
- table,
19
- value
20
- }) => {
21
- if (selectedColumn === void 0 || operator === void 0) {
22
- return null;
23
- }
24
- if (vuuUtils.isDateTimeColumn(selectedColumn)) {
25
- console.log(`return DateInput`);
26
- return /* @__PURE__ */ jsxRuntime.jsx(
27
- FilterClauseValueEditorDate.FilterClauseValueEditorDate,
28
- {
29
- inputProps,
30
- className: cx(`${classBase}Field`, `${classBase}Value`),
31
- "data-field": "value",
32
- value,
33
- operator,
34
- onChangeValue
35
- }
36
- );
37
- }
38
- switch (selectedColumn.serverDataType) {
39
- case "string":
40
- case "char":
41
- return /* @__PURE__ */ jsxRuntime.jsx(
42
- FilterClauseValueEditorText.FilterClauseValueEditorText,
43
- {
44
- inputProps,
45
- className: cx(`${classBase}Field`, `${classBase}Value`),
46
- column: selectedColumn,
47
- onDeselect: onDeselectValue,
48
- onChangeValue,
49
- operator,
50
- suggestionProvider,
51
- table,
52
- value: value === void 0 ? "" : Array.isArray(value) ? value.map((val) => val.toString()) : value.toString()
53
- }
54
- );
55
- case "int":
56
- case "long":
57
- case "double":
12
+ const FilterClauseValueEditor = react.forwardRef(
13
+ function FilterClauseValueEditor2({
14
+ selectedColumn,
15
+ operator,
16
+ inputProps,
17
+ onChangeValue,
18
+ onDeselectValue,
19
+ suggestionProvider,
20
+ table,
21
+ value
22
+ }, forwardedRef) {
23
+ if (selectedColumn === void 0 || operator === void 0) {
24
+ return null;
25
+ }
26
+ if (vuuUtils.isDateTimeColumn(selectedColumn)) {
27
+ console.log(`return DateInput`);
58
28
  return /* @__PURE__ */ jsxRuntime.jsx(
59
- FilterClauseValueEditorNumber.FilterClauseValueEditorNumber,
29
+ FilterClauseValueEditorDate.FilterClauseValueEditorDate,
60
30
  {
61
31
  inputProps,
62
32
  className: cx(`${classBase}Field`, `${classBase}Value`),
63
- column: selectedColumn,
64
33
  "data-field": "value",
65
- onChangeValue,
66
- operator
34
+ value,
35
+ operator,
36
+ onChangeValue
67
37
  }
68
38
  );
69
- default:
70
- console.log("returning null");
71
- return null;
39
+ }
40
+ switch (selectedColumn.serverDataType) {
41
+ case "string":
42
+ case "char":
43
+ return /* @__PURE__ */ jsxRuntime.jsx(
44
+ FilterClauseValueEditorText.FilterClauseValueEditorText,
45
+ {
46
+ inputProps,
47
+ className: cx(`${classBase}Field`, `${classBase}Value`),
48
+ column: selectedColumn,
49
+ onDeselect: onDeselectValue,
50
+ onChangeValue,
51
+ operator,
52
+ ref: forwardedRef,
53
+ suggestionProvider,
54
+ table,
55
+ value: value === void 0 ? "" : Array.isArray(value) ? value.map((val) => val.toString()) : value.toString()
56
+ }
57
+ );
58
+ case "int":
59
+ case "long":
60
+ case "double":
61
+ return /* @__PURE__ */ jsxRuntime.jsx(
62
+ FilterClauseValueEditorNumber.FilterClauseValueEditorNumber,
63
+ {
64
+ inputProps,
65
+ className: cx(`${classBase}Field`, `${classBase}Value`),
66
+ column: selectedColumn,
67
+ "data-field": "value",
68
+ onChangeValue,
69
+ operator,
70
+ ref: forwardedRef
71
+ }
72
+ );
73
+ default:
74
+ console.log("returning null");
75
+ return null;
76
+ }
72
77
  }
73
- };
78
+ );
74
79
 
75
80
  exports.FilterClauseValueEditor = FilterClauseValueEditor;
76
81
  //# sourceMappingURL=FilterClauseValueEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilterClauseValueEditor.js","sources":["../../../src/filter-clause/value-editors/FilterClauseValueEditor.tsx"],"sourcesContent":["import { TableSchemaTable } from \"@vuu-ui/vuu-data-types\";\nimport cx from \"clsx\";\nimport { FilterClauseValueEditorNumber } from \"./FilterClauseValueEditorNumber\";\nimport { FilterClauseValueEditorText } from \"./FilterClauseValueEditorText\";\nimport { useFilterClause } from \"../useFilterClause\";\n\nimport {\n NumericFilterClauseOp,\n SingleValueFilterClauseOp,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { isDateTimeColumn } from \"@vuu-ui/vuu-utils\";\nimport { FilterClauseValueEditorDate } from \"./FilterClauseValueEditorDate\";\nimport { FilterClauseProps } from \"../FilterClause\";\n\nconst classBase = \"vuuFilterClause\";\n\ntype FilterClauseValueEditorProps = Pick<\n ReturnType<typeof useFilterClause>,\n \"selectedColumn\" | \"inputProps\" | \"onChangeValue\" | \"onDeselectValue\"\n> &\n Pick<FilterClauseProps, \"suggestionProvider\"> & {\n table?: TableSchemaTable;\n } & {\n operator?: SingleValueFilterClauseOp | \"in\";\n value?: string | string[] | number | number[] | boolean | boolean[];\n };\n\nexport const FilterClauseValueEditor: React.FC<\n FilterClauseValueEditorProps\n> = ({\n selectedColumn,\n operator,\n inputProps,\n onChangeValue,\n onDeselectValue,\n suggestionProvider,\n table,\n value,\n}) => {\n if (selectedColumn === undefined || operator === undefined) {\n return null;\n }\n\n if (isDateTimeColumn(selectedColumn)) {\n console.log(`return DateInput`);\n return (\n <FilterClauseValueEditorDate\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n data-field=\"value\"\n value={value as number}\n operator={operator as NumericFilterClauseOp}\n onChangeValue={onChangeValue}\n />\n );\n }\n\n switch (selectedColumn.serverDataType) {\n case \"string\":\n case \"char\":\n return (\n <FilterClauseValueEditorText\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n onDeselect={onDeselectValue}\n onChangeValue={onChangeValue}\n operator={operator}\n suggestionProvider={suggestionProvider}\n table={table}\n value={\n value === undefined\n ? \"\"\n : Array.isArray(value)\n ? value.map((val) => val.toString())\n : (value.toString() as string | string[])\n }\n />\n );\n case \"int\":\n case \"long\":\n case \"double\":\n return (\n <FilterClauseValueEditorNumber\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n data-field=\"value\"\n onChangeValue={onChangeValue}\n operator={operator}\n />\n );\n default:\n console.log(\"returning null\");\n return null;\n }\n};\n"],"names":["isDateTimeColumn","jsx","FilterClauseValueEditorDate","FilterClauseValueEditorText","FilterClauseValueEditorNumber"],"mappings":";;;;;;;;;AAcA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAaX,MAAM,0BAET,CAAC;AAAA,EACH,cAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,kBAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AACF,CAAM,KAAA;AACJ,EAAI,IAAA,cAAA,KAAmB,KAAa,CAAA,IAAA,QAAA,KAAa,KAAW,CAAA,EAAA;AAC1D,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAI,IAAAA,yBAAA,CAAiB,cAAc,CAAG,EAAA;AACpC,IAAA,OAAA,CAAQ,IAAI,CAAkB,gBAAA,CAAA,CAAA,CAAA;AAC9B,IACE,uBAAAC,cAAA;AAAA,MAACC,uDAAA;AAAA,MAAA;AAAA,QACC,UAAA;AAAA,QACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,QACtD,YAAW,EAAA,OAAA;AAAA,QACX,KAAA;AAAA,QACA,QAAA;AAAA,QACA,aAAA;AAAA,OAAA;AAAA,KACF,CAAA;AAAA,GAEJ;AAEA,EAAA,QAAQ,eAAe,cAAgB;AAAA,IACrC,KAAK,QAAA,CAAA;AAAA,IACL,KAAK,MAAA;AACH,MACE,uBAAAD,cAAA;AAAA,QAACE,uDAAA;AAAA,QAAA;AAAA,UACC,UAAA;AAAA,UACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,UACtD,MAAQ,EAAA,cAAA;AAAA,UACR,UAAY,EAAA,eAAA;AAAA,UACZ,aAAA;AAAA,UACA,QAAA;AAAA,UACA,kBAAA;AAAA,UACA,KAAA;AAAA,UACA,OACE,KAAU,KAAA,KAAA,CAAA,GACN,EACA,GAAA,KAAA,CAAM,QAAQ,KAAK,CAAA,GACnB,KAAM,CAAA,GAAA,CAAI,CAAC,GAAQ,KAAA,GAAA,CAAI,UAAU,CAAA,GAChC,MAAM,QAAS,EAAA;AAAA,SAAA;AAAA,OAExB,CAAA;AAAA,IAEJ,KAAK,KAAA,CAAA;AAAA,IACL,KAAK,MAAA,CAAA;AAAA,IACL,KAAK,QAAA;AACH,MACE,uBAAAF,cAAA;AAAA,QAACG,2DAAA;AAAA,QAAA;AAAA,UACC,UAAA;AAAA,UACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,UACtD,MAAQ,EAAA,cAAA;AAAA,UACR,YAAW,EAAA,OAAA;AAAA,UACX,aAAA;AAAA,UACA,QAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,IAEJ;AACE,MAAA,OAAA,CAAQ,IAAI,gBAAgB,CAAA,CAAA;AAC5B,MAAO,OAAA,IAAA,CAAA;AAAA,GACX;AACF;;;;"}
1
+ {"version":3,"file":"FilterClauseValueEditor.js","sources":["../../../src/filter-clause/value-editors/FilterClauseValueEditor.tsx"],"sourcesContent":["import { TableSchemaTable } from \"@vuu-ui/vuu-data-types\";\nimport cx from \"clsx\";\nimport { useFilterClause } from \"../useFilterClause\";\nimport { FilterClauseValueEditorNumber } from \"./FilterClauseValueEditorNumber\";\nimport { FilterClauseValueEditorText } from \"./FilterClauseValueEditorText\";\n\nimport {\n NumericFilterClauseOp,\n SingleValueFilterClauseOp,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { isDateTimeColumn } from \"@vuu-ui/vuu-utils\";\nimport { ForwardedRef, forwardRef } from \"react\";\nimport { FilterClauseProps } from \"../FilterClause\";\nimport { FilterClauseValueEditorDate } from \"./FilterClauseValueEditorDate\";\n\nconst classBase = \"vuuFilterClause\";\n\ntype FilterClauseValueEditorProps = Pick<\n ReturnType<typeof useFilterClause>,\n \"selectedColumn\" | \"inputProps\" | \"onChangeValue\" | \"onDeselectValue\"\n> &\n Pick<FilterClauseProps, \"suggestionProvider\"> & {\n table?: TableSchemaTable;\n } & {\n operator?: SingleValueFilterClauseOp | \"in\";\n value?: string | string[] | number | number[] | boolean | boolean[];\n };\n\nexport const FilterClauseValueEditor = forwardRef(\n function FilterClauseValueEditor(\n {\n selectedColumn,\n operator,\n inputProps,\n onChangeValue,\n onDeselectValue,\n suggestionProvider,\n table,\n value,\n }: FilterClauseValueEditorProps,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) {\n if (selectedColumn === undefined || operator === undefined) {\n return null;\n }\n\n if (isDateTimeColumn(selectedColumn)) {\n console.log(`return DateInput`);\n return (\n <FilterClauseValueEditorDate\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n data-field=\"value\"\n // ref={forwardedRef}\n value={value as number}\n operator={operator as NumericFilterClauseOp}\n onChangeValue={onChangeValue}\n />\n );\n }\n\n switch (selectedColumn.serverDataType) {\n case \"string\":\n case \"char\":\n return (\n <FilterClauseValueEditorText\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n onDeselect={onDeselectValue}\n onChangeValue={onChangeValue}\n operator={operator}\n ref={forwardedRef}\n suggestionProvider={suggestionProvider}\n table={table}\n value={\n value === undefined\n ? \"\"\n : Array.isArray(value)\n ? value.map((val) => val.toString())\n : (value.toString() as string | string[])\n }\n />\n );\n case \"int\":\n case \"long\":\n case \"double\":\n return (\n <FilterClauseValueEditorNumber\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n data-field=\"value\"\n onChangeValue={onChangeValue}\n operator={operator}\n ref={forwardedRef}\n />\n );\n default:\n console.log(\"returning null\");\n return null;\n }\n }\n);\n"],"names":["forwardRef","FilterClauseValueEditor","isDateTimeColumn","jsx","FilterClauseValueEditorDate","FilterClauseValueEditorText","FilterClauseValueEditorNumber"],"mappings":";;;;;;;;;;AAeA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAaX,MAAM,uBAA0B,GAAAA,gBAAA;AAAA,EACrC,SAASC,wBACP,CAAA;AAAA,IACE,cAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA,aAAA;AAAA,IACA,eAAA;AAAA,IACA,kBAAA;AAAA,IACA,KAAA;AAAA,IACA,KAAA;AAAA,KAEF,YACA,EAAA;AACA,IAAI,IAAA,cAAA,KAAmB,KAAa,CAAA,IAAA,QAAA,KAAa,KAAW,CAAA,EAAA;AAC1D,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AAEA,IAAI,IAAAC,yBAAA,CAAiB,cAAc,CAAG,EAAA;AACpC,MAAA,OAAA,CAAQ,IAAI,CAAkB,gBAAA,CAAA,CAAA,CAAA;AAC9B,MACE,uBAAAC,cAAA;AAAA,QAACC,uDAAA;AAAA,QAAA;AAAA,UACC,UAAA;AAAA,UACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,UACtD,YAAW,EAAA,OAAA;AAAA,UAEX,KAAA;AAAA,UACA,QAAA;AAAA,UACA,aAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,KAEJ;AAEA,IAAA,QAAQ,eAAe,cAAgB;AAAA,MACrC,KAAK,QAAA,CAAA;AAAA,MACL,KAAK,MAAA;AACH,QACE,uBAAAD,cAAA;AAAA,UAACE,uDAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,YACtD,MAAQ,EAAA,cAAA;AAAA,YACR,UAAY,EAAA,eAAA;AAAA,YACZ,aAAA;AAAA,YACA,QAAA;AAAA,YACA,GAAK,EAAA,YAAA;AAAA,YACL,kBAAA;AAAA,YACA,KAAA;AAAA,YACA,OACE,KAAU,KAAA,KAAA,CAAA,GACN,EACA,GAAA,KAAA,CAAM,QAAQ,KAAK,CAAA,GACnB,KAAM,CAAA,GAAA,CAAI,CAAC,GAAQ,KAAA,GAAA,CAAI,UAAU,CAAA,GAChC,MAAM,QAAS,EAAA;AAAA,WAAA;AAAA,SAExB,CAAA;AAAA,MAEJ,KAAK,KAAA,CAAA;AAAA,MACL,KAAK,MAAA,CAAA;AAAA,MACL,KAAK,QAAA;AACH,QACE,uBAAAF,cAAA;AAAA,UAACG,2DAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,YACtD,MAAQ,EAAA,cAAA;AAAA,YACR,YAAW,EAAA,OAAA;AAAA,YACX,aAAA;AAAA,YACA,QAAA;AAAA,YACA,GAAK,EAAA,YAAA;AAAA,WAAA;AAAA,SACP,CAAA;AAAA,MAEJ;AACE,QAAA,OAAA,CAAQ,IAAI,gBAAgB,CAAA,CAAA;AAC5B,QAAO,OAAA,IAAA,CAAA;AAAA,KACX;AAAA,GACF;AACF;;;;"}
@@ -1,4 +1,4 @@
1
- var filterClauseCss = ".vuuFilterClause {\n --vuuExpandoInput-top: 0;\n --content-height: calc(var(--salt-size-base) - var(--salt-spacing-150));\n --vuuExpandoInput-height: var(--content-height);\n --vuuExpandoCombobox-height: var(--content-height);\n --saltButton-height: 16px;\n --saltButton-width: 16px;\n --salt-focused-outlineStyle: dotted;\n\n align-items: center;\n background: var(--salt-container-primary-background);\n border-color: var(--vuu-color-gray-45);\n border-radius: var(--saltButton-borderRadius);\n border-width: 1px;\n border-style: solid;\n display: flex;\n flex-direction: row;\n gap: var(--salt-spacing-50);\n height: var(--salt-size-base);\n min-width: calc(var(--salt-size-base) * 4);\n padding: 0 var(--salt-spacing-100);\n width: fit-content;\n\n\n .saltComboBox-focused {\n outline: none;\n }\n}\n\n.vuuFilterClause:focus-within {\n border-color: var(--vuu-color-purple-10);\n}\n\n.vuu-density-high .vuuFilterClause {\n padding: 4px 8px;\n gap: 4px;\n --salt-text-lineHeight: 12px;\n --saltInputLegacy-fontSize: 12px;\n --saltInputLegacy-minWidth: 12px;\n}\n\n.vuu-density-high .vuuFilterClause .saltInput {\n padding: 0;\n min-height: 16px;\n height: 16px;\n}\n\n.vuuFilterClause .vuuExpandoCombobox {\n flex-basis: auto;\n flex-shrink: 0;\n flex-grow: 0;\n}\n\n.vuuFilterClauseOperator-hidden {\n display: none;\n}\n\n\n.vuuFilterClause .saltInput-focused,\n.vuuFilterClause .saltTokenizedInput-focused {\n outline: none;\n color: var(--salt-content-primary-foreground);\n}\n\n.vuu-theme .saltList {\n --list-borderWidth: 1px;\n --list-borderStyle: solid;\n border-radius: 4px;\n box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.4);\n}\n\n.saltListItem[aria-selected=\"true\"]:not(.saltListItem-checkbox) {\n --list-item-background: var(--list-item-background-active);\n color: var(--list-item-text-color-active);\n}\n\n.saltTokenizedInput {\n height: 16px;\n min-height: 16px;\n}\n\n.saltTokenizedInput .saltInputPill {\n --pill-fontSize: 12px;\n --saltButton-borderStyle: none;\n --pill-background: none;\n height: 16px;\n margin: 0;\n}\n\n.saltTokenizedInput-pillGroup {\n padding: 0;\n height: 16px;\n}\n\n.vuuFilterClause-DatePicker {\n border: none;\n}\n";
1
+ var filterClauseCss = ".vuuFilterClause {\n --vuuExpandoInput-top: 0;\n --content-height: calc(var(--salt-size-base) - var(--salt-spacing-150));\n --vuuExpandoInput-height: var(--content-height);\n --vuuExpandoCombobox-height: var(--content-height);\n --saltButton-height: 16px;\n --saltButton-width: 16px;\n --salt-focused-outlineStyle: dotted;\n\n align-items: center;\n background: var(--salt-container-primary-background);\n border-color: var(--vuu-color-gray-45);\n border-radius: var(--saltButton-borderRadius);\n border-width: 1px;\n border-style: solid;\n display: flex;\n flex-direction: row;\n gap: var(--salt-spacing-50);\n height: var(--salt-size-base);\n min-width: calc(var(--salt-size-base) * 4);\n padding: 0 var(--salt-spacing-100);\n width: fit-content;\n\n .vuuFilterClauseField {\n flex: 0 0 auto;\n }\n .vuuFilterClauseField:last-child {\n flex: 1 0 auto;\n }\n\n .vuuExpandoCombobox {\n flex-basis: auto;\n flex-shrink: 0;\n flex-grow: 0;\n }\n\n &:focus-within {\n border-color: var(--vuu-color-purple-10);\n }\n\n .saltComboBox-focused {\n outline: none;\n }\n\n .saltTokenizedInput {\n height: 16px;\n min-height: 16px;\n }\n\n .saltTokenizedInput .saltInputPill {\n --pill-fontSize: 12px;\n --saltButton-borderStyle: none;\n --pill-background: none;\n height: 16px;\n margin: 0;\n }\n\n .saltTokenizedInput-pillGroup {\n padding: 0;\n height: 16px;\n }\n}\n\n.salt-density-high .vuuFilterClause {\n padding: 4px 8px;\n gap: 4px;\n --salt-text-lineHeight: 12px;\n --saltInputLegacy-fontSize: 12px;\n --saltInputLegacy-minWidth: 12px;\n}\n\n.salt-density-high .vuuFilterClause .saltInput {\n padding: 0;\n min-height: 16px;\n height: 16px;\n}\n\n.vuuFilterClauseOperator-hidden {\n display: none;\n}\n\n.vuuFilterClause .saltInput-focused,\n.vuuFilterClause .saltTokenizedInput-focused {\n outline: none;\n color: var(--salt-content-primary-foreground);\n}\n\n.vuuFilterClause-DatePicker {\n border: none;\n}\n";
2
2
 
3
3
  export { filterClauseCss as default };
4
4
  //# sourceMappingURL=FilterClause.css.js.map
@@ -30,10 +30,10 @@ const FilterClause = ({
30
30
  // onChangeColumn,
31
31
  onSelectColumn,
32
32
  onSelectOperator,
33
- onFocus,
34
33
  onDeselectValue,
35
34
  operatorRef,
36
- selectedColumn
35
+ selectedColumn,
36
+ valueRef
37
37
  } = useFilterClause({
38
38
  filterClauseModel,
39
39
  onCancel,
@@ -47,57 +47,49 @@ const FilterClause = ({
47
47
  window: targetWindow
48
48
  });
49
49
  const columns = useMemo(() => Object.values(columnsByName), [columnsByName]);
50
- return /* @__PURE__ */ jsxs(
51
- "div",
52
- {
53
- className: cx(classBase, className),
54
- ...htmlAttributes,
55
- onFocus,
56
- tabIndex: 0,
57
- children: [
58
- /* @__PURE__ */ jsx(
59
- ColumnPicker,
60
- {
61
- inputProps,
62
- className: cx(`${classBase}Field`, `${classBase}Column`),
63
- columns,
64
- onSelect: onSelectColumn,
65
- ref: columnRef,
66
- value: filterClause.column ?? ""
67
- },
68
- "column-field"
69
- ),
70
- selectedColumn?.name ? /* @__PURE__ */ jsx(
71
- OperatorPicker,
72
- {
73
- column: selectedColumn,
74
- inputProps,
75
- className: cx(`${classBase}Field`, `${classBase}Operator`, {
76
- [`${classBase}Operator-hidden`]: selectedColumn === null
77
- }),
78
- onSelect: onSelectOperator,
79
- ref: operatorRef,
80
- value: filterClause.op ?? ""
81
- },
82
- "operator-field"
83
- ) : null,
84
- /* @__PURE__ */ jsx(
85
- FilterClauseValueEditor,
86
- {
87
- inputProps,
88
- onChangeValue,
89
- onDeselectValue,
90
- operator: filterClause.op,
91
- selectedColumn,
92
- suggestionProvider,
93
- table: tableSchema.table,
94
- value: filterClause?.values ?? filterClause?.value
95
- },
96
- "value-field"
97
- )
98
- ]
99
- }
100
- );
50
+ return /* @__PURE__ */ jsxs("div", { className: cx(classBase, className), ...htmlAttributes, tabIndex: 0, children: [
51
+ /* @__PURE__ */ jsx(
52
+ ColumnPicker,
53
+ {
54
+ inputProps,
55
+ className: cx(`${classBase}Field`, `${classBase}Column`),
56
+ columns,
57
+ onSelect: onSelectColumn,
58
+ ref: columnRef,
59
+ value: filterClauseModel.column ?? ""
60
+ },
61
+ "column-field"
62
+ ),
63
+ selectedColumn?.name ? /* @__PURE__ */ jsx(
64
+ OperatorPicker,
65
+ {
66
+ column: selectedColumn,
67
+ inputProps,
68
+ className: cx(`${classBase}Field`, `${classBase}Operator`, {
69
+ [`${classBase}Operator-hidden`]: selectedColumn === null
70
+ }),
71
+ onSelect: onSelectOperator,
72
+ ref: operatorRef,
73
+ value: filterClauseModel.op ?? ""
74
+ },
75
+ "operator-field"
76
+ ) : null,
77
+ filterClauseModel.op ? /* @__PURE__ */ jsx(
78
+ FilterClauseValueEditor,
79
+ {
80
+ inputProps,
81
+ onChangeValue,
82
+ onDeselectValue,
83
+ operator: filterClauseModel.op,
84
+ ref: valueRef,
85
+ selectedColumn,
86
+ suggestionProvider,
87
+ table: tableSchema.table,
88
+ value: filterClause?.values ?? filterClause?.value
89
+ },
90
+ "value-field"
91
+ ) : null
92
+ ] });
101
93
  };
102
94
 
103
95
  export { FilterClause };
@@ -1 +1 @@
1
- {"version":3,"file":"FilterClause.js","sources":["../../src/filter-clause/FilterClause.tsx"],"sourcesContent":["import type { SuggestionProvider, TableSchema } from \"@vuu-ui/vuu-data-types\";\nimport {\n ColumnDescriptorsByName,\n MultiValueFilterClause,\n SingleValueFilterClause,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { CloseReason } from \"@vuu-ui/vuu-ui-controls\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport cx from \"clsx\";\nimport { HTMLAttributes, useMemo } from \"react\";\nimport { FilterClauseModel } from \"../FilterModel\";\nimport { useFilterClause } from \"./useFilterClause\";\nimport { FilterClauseValueEditor } from \"./value-editors/FilterClauseValueEditor\";\nimport { ColumnPicker } from \"./ColumnPicker\";\n\nimport filterClauseCss from \"./FilterClause.css\";\nimport { OperatorPicker } from \"./OperatorPicker\";\n\nexport type FilterClauseCancelType = \"Backspace\" | \"Escape\";\nexport type FilterClauseCancelHandler = (\n filterClause: FilterClauseModel,\n reason: FilterClauseCancelType\n) => void;\n\nexport interface FilterClauseProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n columnsByName: ColumnDescriptorsByName;\n filterClauseModel: FilterClauseModel;\n onCancel?: FilterClauseCancelHandler;\n onDropdownClose?: (closeReason: CloseReason) => void;\n onDropdownOpen?: () => void;\n onFocusSave?: () => void;\n suggestionProvider?: SuggestionProvider;\n tableSchema: TableSchema;\n}\n\nconst classBase = \"vuuFilterClause\";\n\nexport const FilterClause = ({\n className,\n columnsByName,\n onCancel,\n onDropdownClose,\n onDropdownOpen,\n onFocusSave,\n filterClauseModel,\n suggestionProvider,\n tableSchema,\n ...htmlAttributes\n}: FilterClauseProps) => {\n const {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue,\n // onChangeColumn,\n onSelectColumn,\n onSelectOperator,\n onFocus,\n onDeselectValue,\n operatorRef,\n selectedColumn,\n } = useFilterClause({\n filterClauseModel,\n onCancel,\n onFocusSave,\n columnsByName,\n });\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-filter-clause\",\n css: filterClauseCss,\n window: targetWindow,\n });\n\n const columns = useMemo(() => Object.values(columnsByName), [columnsByName]);\n\n return (\n <div\n className={cx(classBase, className)}\n {...htmlAttributes}\n onFocus={onFocus}\n tabIndex={0}\n >\n <ColumnPicker\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Column`)}\n columns={columns}\n key=\"column-field\"\n onSelect={onSelectColumn}\n ref={columnRef}\n value={filterClause.column ?? \"\"}\n />\n {selectedColumn?.name ? (\n <OperatorPicker\n column={selectedColumn}\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Operator`, {\n [`${classBase}Operator-hidden`]: selectedColumn === null,\n })}\n key=\"operator-field\"\n onSelect={onSelectOperator}\n ref={operatorRef}\n value={filterClause.op ?? \"\"}\n />\n ) : null}\n <FilterClauseValueEditor\n inputProps={inputProps}\n key=\"value-field\"\n onChangeValue={onChangeValue}\n onDeselectValue={onDeselectValue}\n operator={filterClause.op}\n selectedColumn={selectedColumn}\n suggestionProvider={suggestionProvider}\n table={tableSchema.table}\n value={\n (filterClause as MultiValueFilterClause)?.values ??\n (filterClause as SingleValueFilterClause)?.value\n }\n />\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAqCA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAEX,MAAM,eAAe,CAAC;AAAA,EAC3B,SAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,iBAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,GAAG,cAAA;AACL,CAAyB,KAAA;AACvB,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA;AAAA,IAEA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,MACE,eAAgB,CAAA;AAAA,IAClB,iBAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,GACD,CAAA,CAAA;AAED,EAAA,MAAM,eAAe,SAAU,EAAA,CAAA;AAC/B,EAAyB,wBAAA,CAAA;AAAA,IACvB,MAAQ,EAAA,mBAAA;AAAA,IACR,GAAK,EAAA,eAAA;AAAA,IACL,MAAQ,EAAA,YAAA;AAAA,GACT,CAAA,CAAA;AAED,EAAM,MAAA,OAAA,GAAU,QAAQ,MAAM,MAAA,CAAO,OAAO,aAAa,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA,CAAA;AAE3E,EACE,uBAAA,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAG,CAAA,SAAA,EAAW,SAAS,CAAA;AAAA,MACjC,GAAG,cAAA;AAAA,MACJ,OAAA;AAAA,MACA,QAAU,EAAA,CAAA;AAAA,MAEV,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,YAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAQ,MAAA,CAAA,CAAA;AAAA,YACvD,OAAA;AAAA,YAEA,QAAU,EAAA,cAAA;AAAA,YACV,GAAK,EAAA,SAAA;AAAA,YACL,KAAA,EAAO,aAAa,MAAU,IAAA,EAAA;AAAA,WAAA;AAAA,UAH1B,cAAA;AAAA,SAIN;AAAA,QACC,gBAAgB,IACf,mBAAA,GAAA;AAAA,UAAC,cAAA;AAAA,UAAA;AAAA,YACC,MAAQ,EAAA,cAAA;AAAA,YACR,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAY,QAAA,CAAA,EAAA;AAAA,cACzD,CAAC,CAAA,EAAG,SAAS,CAAA,eAAA,CAAiB,GAAG,cAAmB,KAAA,IAAA;AAAA,aACrD,CAAA;AAAA,YAED,QAAU,EAAA,gBAAA;AAAA,YACV,GAAK,EAAA,WAAA;AAAA,YACL,KAAA,EAAO,aAAa,EAAM,IAAA,EAAA;AAAA,WAAA;AAAA,UAHtB,gBAAA;AAAA,SAKJ,GAAA,IAAA;AAAA,wBACJ,GAAA;AAAA,UAAC,uBAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YAEA,aAAA;AAAA,YACA,eAAA;AAAA,YACA,UAAU,YAAa,CAAA,EAAA;AAAA,YACvB,cAAA;AAAA,YACA,kBAAA;AAAA,YACA,OAAO,WAAY,CAAA,KAAA;AAAA,YACnB,KAAA,EACG,YAAyC,EAAA,MAAA,IACzC,YAA0C,EAAA,KAAA;AAAA,WAAA;AAAA,UATzC,aAAA;AAAA,SAWN;AAAA,OAAA;AAAA,KAAA;AAAA,GACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"FilterClause.js","sources":["../../src/filter-clause/FilterClause.tsx"],"sourcesContent":["import type { SuggestionProvider, TableSchema } from \"@vuu-ui/vuu-data-types\";\nimport {\n ColumnDescriptorsByName,\n MultiValueFilterClause,\n SingleValueFilterClause,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { CloseReason } from \"@vuu-ui/vuu-ui-controls\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport cx from \"clsx\";\nimport { HTMLAttributes, useMemo } from \"react\";\nimport { FilterClauseModel } from \"../FilterModel\";\nimport { useFilterClause } from \"./useFilterClause\";\nimport { FilterClauseValueEditor } from \"./value-editors/FilterClauseValueEditor\";\nimport { ColumnPicker } from \"./ColumnPicker\";\n\nimport filterClauseCss from \"./FilterClause.css\";\nimport { OperatorPicker } from \"./OperatorPicker\";\n\nexport type FilterClauseCancelType = \"Backspace\" | \"Escape\";\nexport type FilterClauseCancelHandler = (\n filterClause: FilterClauseModel,\n reason: FilterClauseCancelType\n) => void;\n\nexport interface FilterClauseProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n columnsByName: ColumnDescriptorsByName;\n filterClauseModel: FilterClauseModel;\n onCancel?: FilterClauseCancelHandler;\n onDropdownClose?: (closeReason: CloseReason) => void;\n onDropdownOpen?: () => void;\n onFocusSave?: () => void;\n suggestionProvider?: SuggestionProvider;\n tableSchema: TableSchema;\n}\n\nconst classBase = \"vuuFilterClause\";\n\nexport const FilterClause = ({\n className,\n columnsByName,\n onCancel,\n onDropdownClose,\n onDropdownOpen,\n onFocusSave,\n filterClauseModel,\n suggestionProvider,\n tableSchema,\n ...htmlAttributes\n}: FilterClauseProps) => {\n const {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue,\n // onChangeColumn,\n onSelectColumn,\n onSelectOperator,\n onDeselectValue,\n operatorRef,\n selectedColumn,\n valueRef,\n } = useFilterClause({\n filterClauseModel,\n onCancel,\n onFocusSave,\n columnsByName,\n });\n\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-filter-clause\",\n css: filterClauseCss,\n window: targetWindow,\n });\n\n const columns = useMemo(() => Object.values(columnsByName), [columnsByName]);\n\n return (\n <div className={cx(classBase, className)} {...htmlAttributes} tabIndex={0}>\n <ColumnPicker\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Column`)}\n columns={columns}\n key=\"column-field\"\n onSelect={onSelectColumn}\n ref={columnRef}\n value={filterClauseModel.column ?? \"\"}\n />\n {selectedColumn?.name ? (\n <OperatorPicker\n column={selectedColumn}\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Operator`, {\n [`${classBase}Operator-hidden`]: selectedColumn === null,\n })}\n key=\"operator-field\"\n onSelect={onSelectOperator}\n ref={operatorRef}\n value={filterClauseModel.op ?? \"\"}\n />\n ) : null}\n {filterClauseModel.op ? (\n <FilterClauseValueEditor\n inputProps={inputProps}\n key=\"value-field\"\n onChangeValue={onChangeValue}\n onDeselectValue={onDeselectValue}\n operator={filterClauseModel.op}\n ref={valueRef}\n selectedColumn={selectedColumn}\n suggestionProvider={suggestionProvider}\n table={tableSchema.table}\n value={\n (filterClause as MultiValueFilterClause)?.values ??\n (filterClause as SingleValueFilterClause)?.value\n }\n />\n ) : null}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAqCA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAEX,MAAM,eAAe,CAAC;AAAA,EAC3B,SAAA;AAAA,EACA,aAAA;AAAA,EACA,QAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,iBAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,GAAG,cAAA;AACL,CAAyB,KAAA;AACvB,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,aAAA;AAAA;AAAA,IAEA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,eAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA,QAAA;AAAA,MACE,eAAgB,CAAA;AAAA,IAClB,iBAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,GACD,CAAA,CAAA;AAED,EAAA,MAAM,eAAe,SAAU,EAAA,CAAA;AAC/B,EAAyB,wBAAA,CAAA;AAAA,IACvB,MAAQ,EAAA,mBAAA;AAAA,IACR,GAAK,EAAA,eAAA;AAAA,IACL,MAAQ,EAAA,YAAA;AAAA,GACT,CAAA,CAAA;AAED,EAAM,MAAA,OAAA,GAAU,QAAQ,MAAM,MAAA,CAAO,OAAO,aAAa,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA,CAAA;AAE3E,EACE,uBAAA,IAAA,CAAC,KAAI,EAAA,EAAA,SAAA,EAAW,EAAG,CAAA,SAAA,EAAW,SAAS,CAAI,EAAA,GAAG,cAAgB,EAAA,QAAA,EAAU,CACtE,EAAA,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACC,UAAA;AAAA,QACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAQ,MAAA,CAAA,CAAA;AAAA,QACvD,OAAA;AAAA,QAEA,QAAU,EAAA,cAAA;AAAA,QACV,GAAK,EAAA,SAAA;AAAA,QACL,KAAA,EAAO,kBAAkB,MAAU,IAAA,EAAA;AAAA,OAAA;AAAA,MAH/B,cAAA;AAAA,KAIN;AAAA,IACC,gBAAgB,IACf,mBAAA,GAAA;AAAA,MAAC,cAAA;AAAA,MAAA;AAAA,QACC,MAAQ,EAAA,cAAA;AAAA,QACR,UAAA;AAAA,QACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAY,QAAA,CAAA,EAAA;AAAA,UACzD,CAAC,CAAA,EAAG,SAAS,CAAA,eAAA,CAAiB,GAAG,cAAmB,KAAA,IAAA;AAAA,SACrD,CAAA;AAAA,QAED,QAAU,EAAA,gBAAA;AAAA,QACV,GAAK,EAAA,WAAA;AAAA,QACL,KAAA,EAAO,kBAAkB,EAAM,IAAA,EAAA;AAAA,OAAA;AAAA,MAH3B,gBAAA;AAAA,KAKJ,GAAA,IAAA;AAAA,IACH,kBAAkB,EACjB,mBAAA,GAAA;AAAA,MAAC,uBAAA;AAAA,MAAA;AAAA,QACC,UAAA;AAAA,QAEA,aAAA;AAAA,QACA,eAAA;AAAA,QACA,UAAU,iBAAkB,CAAA,EAAA;AAAA,QAC5B,GAAK,EAAA,QAAA;AAAA,QACL,cAAA;AAAA,QACA,kBAAA;AAAA,QACA,OAAO,WAAY,CAAA,KAAA;AAAA,QACnB,KAAA,EACG,YAAyC,EAAA,MAAA,IACzC,YAA0C,EAAA,KAAA;AAAA,OAAA;AAAA,MAVzC,aAAA;AAAA,KAaJ,GAAA,IAAA;AAAA,GACN,EAAA,CAAA,CAAA;AAEJ;;;;"}
@@ -185,5 +185,5 @@ const focusFirstClauseIfAllClausesValid = (filterEditor) => {
185
185
  }
186
186
  };
187
187
 
188
- export { clauseIsNotFirst, elementIsFilterClause, elementIsFilterCombinator, focusField, focusFilterClauseField, focusFirstClauseIfAllClausesValid, focusLastClauseValue, focusNextElement, focusNextFocusableElement, getFocusedFieldDetails, navigateToNextFilterClause, navigateToNextItemIfAtBoundary, tabToPreviousFilterCombinator };
188
+ export { clauseIsNotFirst, elementIsFilterClause, elementIsFilterCombinator, focusFilterClauseField, focusFirstClauseIfAllClausesValid, focusLastClauseValue, focusNextElement, focusNextFocusableElement, getFocusedFieldDetails, navigateToNextFilterClause, navigateToNextItemIfAtBoundary, tabToPreviousFilterCombinator };
189
189
  //# sourceMappingURL=filterClauseFocusManagement.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filterClauseFocusManagement.js","sources":["../../src/filter-clause/filterClauseFocusManagement.ts"],"sourcesContent":["import { getElementDataIndex, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { KeyboardEvent } from \"react\";\n\nconst getFilterClauseElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClause\") as HTMLElement;\n\nconst getFilterClauseFieldElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClauseField\") as HTMLElement;\n\ntype FilterClauseFieldName = \"column\" | \"operator\" | \"value\";\nconst mapFilterFieldToClassName: Record<FilterClauseFieldName, string> = {\n column: \"vuuFilterClauseColumn\",\n operator: \"vuuFilterClauseOperator\",\n value: \"vuuFilterClauseValue\",\n};\n\nconst getFilterClauseDetails = ({ classList }: HTMLElement) => {\n if (classList.contains(\"vuuFilterClauseColumn\")) {\n return \"column\";\n } else if (classList.contains(\"vuuFilterClauseOperator\")) {\n return \"operator\";\n } else if (classList.contains(\"vuuFilterClauseValue\")) {\n return \"value\";\n } else {\n throw Error(\n \"getFilterClauseField, filterClauseElemnent is missing required class name\"\n );\n }\n};\n\nexport const getFocusedFieldDetails = (): [number, string] | [] => {\n const el = document.activeElement as HTMLElement;\n const field = queryClosest(el, \".vuuFilterClauseField\");\n const filterClause = queryClosest(field, \".vuuFilterClause\");\n if (filterClause && field) {\n return [getElementDataIndex(filterClause), getFilterClauseDetails(field)];\n } else {\n return [];\n }\n};\n\n// Focus the input control within field. If clause passed, will\n// focus first field within clause\nexport const focusField = (fieldOrClause: HTMLElement | null) => {\n const input = fieldOrClause?.querySelector(\"input\");\n if (input) {\n input.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n }\n};\n\nexport const elementIsFilterCombinator = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClauseCombinator\");\n\nexport const elementIsFilterClause = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClause\");\n\nexport const focusFilterClauseField = (\n filterEditor: HTMLElement,\n filterClauseIndex: number,\n fieldName: FilterClauseFieldName = \"value\"\n) => {\n if (filterEditor) {\n const fieldClassName = mapFilterFieldToClassName[fieldName];\n const field = filterEditor.querySelector(\n `.vuuFilterClause[data-index=\"${filterClauseIndex}\"] .${fieldClassName}`\n ) as HTMLElement;\n focusField(field);\n }\n};\n\nexport const focusLastClauseValue = (filterEditor: HTMLElement) => {\n console.log(\"focusLastClauseValue\");\n const field = Array.from(\n filterEditor?.querySelectorAll(\".vuuFilterClauseField\")\n ).at(-1) as HTMLElement;\n focusField(field);\n};\n\nexport const focusNextFocusableElement = (direction: \"fwd\" | \"bwd\" = \"fwd\") => {\n const activeField = getFocusedField() as HTMLElement;\n const filterClause = getFilterClauseElement(activeField);\n if (direction === \"fwd\" && filterClause?.lastChild === activeField) {\n requestAnimationFrame(() => {\n focusNextFocusableElement();\n });\n } else {\n const nextField =\n direction === \"fwd\"\n ? (activeField?.nextElementSibling as HTMLElement)\n : (activeField?.previousElementSibling as HTMLElement);\n\n nextField?.querySelector(\"input\")?.focus();\n }\n};\n\nconst getFocusedField = () => {\n const activeElement = document.activeElement as HTMLElement;\n if (activeElement?.classList.contains(\"vuuFilterClause-clearButton\")) {\n return activeElement as HTMLElement;\n } else {\n return getFilterClauseFieldElement(activeElement);\n }\n};\n\nexport const focusNextElement = () => {\n const filterClauseField = getFocusedField();\n const filterClause = getFilterClauseElement(filterClauseField);\n if (filterClause && filterClauseField) {\n if (filterClauseField.classList.contains(\"vuuFilterClauseValue\")) {\n const clearButton = filterClause.querySelector(\n \".vuuFilterClause-clearButton\"\n ) as HTMLButtonElement;\n clearButton?.focus();\n } else {\n focusNextFocusableElement();\n }\n }\n};\n\nconst cursorAtTextStart = (input: HTMLInputElement) =>\n input.selectionStart === 0;\n\nconst cursorAtTextEnd = (input: HTMLInputElement) =>\n input.selectionStart === input.value.length;\n\nconst getFieldName = (field: HTMLElement) =>\n field?.classList.contains(\"vuuFilterClauseColumn\")\n ? \"column\"\n : field?.classList.contains(\"vuuFilterClauseOperator\")\n ? \"operator\"\n : \"value\";\n\nexport const clauseIsNotFirst = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n if (clause) {\n const index = getElementDataIndex(clause);\n return index > 0;\n }\n};\n\nconst clauseIsNotLast = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n const nextClause = clause?.nextElementSibling as HTMLElement;\n return nextClause?.classList.contains(\"vuuFilterClauseCombinator\");\n};\n\nexport const tabToPreviousFilterCombinator = (currentElement: HTMLElement) => {\n const filterClause = getFilterClauseElement(currentElement);\n const nextItem = filterClause.previousSibling as HTMLElement;\n console.log(`tab to previous combinator`);\n nextItem?.focus();\n};\n\nexport const navigateToNextFilterClause = (\n currentElement: HTMLElement,\n direction: \"bwd\" | \"fwd\" = \"fwd\"\n) => {\n if (direction === \"bwd\") {\n if (elementIsFilterCombinator(currentElement)) {\n const nextClause = currentElement.previousElementSibling;\n if (elementIsFilterClause(nextClause)) {\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n console.log(`focus field Value ${nextField?.classList}`);\n focusField(nextField);\n }\n } else {\n const filterClause = getFilterClauseElement(currentElement);\n const nextClause = filterClause.previousSibling as HTMLElement;\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n focusField(nextField);\n }\n } else {\n const nextClause = currentElement.nextSibling as HTMLElement;\n focusField(nextClause);\n }\n};\n\n// The logic around preventDefault/stopPropagation is important\n// in this function\nexport const navigateToNextItemIfAtBoundary = (\n evt: KeyboardEvent<HTMLInputElement>\n) => {\n const input = evt.target as HTMLInputElement;\n const field = getFilterClauseFieldElement(input);\n if (evt.key === \"ArrowLeft\") {\n if (cursorAtTextStart(input)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"column\") {\n if (clauseIsNotFirst(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.previousElementSibling as HTMLElement;\n combinator?.focus();\n }\n } else {\n evt.preventDefault();\n focusField(field.previousSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at start. We want the arrowLeft to move the cursor\n evt.stopPropagation();\n } else if (evt.key === \"ArrowRight\") {\n if (cursorAtTextEnd(input as HTMLInputElement)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"value\") {\n if (clauseIsNotLast(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.nextElementSibling as HTMLElement;\n combinator?.focus();\n }\n // Do not preventDefault, stopPropagation\n return;\n } else {\n evt.preventDefault();\n focusField(field.nextSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at end. We want the arrowRight to move the cursor\n evt.stopPropagation();\n }\n};\n\nexport const focusFirstClauseIfAllClausesValid = (\n filterEditor: HTMLElement\n) => {\n const columInput = Array.from(\n filterEditor.querySelectorAll(\".vuuFilterClauseColumn input\")\n ) as HTMLInputElement[];\n if (columInput.every((input) => input.value.length > 0)) {\n setTimeout(() => {\n const input = columInput.at(0);\n input?.select();\n input?.focus();\n }, 100);\n }\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"filterClauseFocusManagement.js","sources":["../../src/filter-clause/filterClauseFocusManagement.ts"],"sourcesContent":["import { getElementDataIndex, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { KeyboardEvent } from \"react\";\n\nconst getFilterClauseElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClause\") as HTMLElement;\n\nconst getFilterClauseFieldElement = (possiblyDescendant?: HTMLElement) =>\n possiblyDescendant?.closest(\".vuuFilterClauseField\") as HTMLElement;\n\ntype FilterClauseFieldName = \"column\" | \"operator\" | \"value\";\nconst mapFilterFieldToClassName: Record<FilterClauseFieldName, string> = {\n column: \"vuuFilterClauseColumn\",\n operator: \"vuuFilterClauseOperator\",\n value: \"vuuFilterClauseValue\",\n};\n\nconst getFilterClauseDetails = ({ classList }: HTMLElement) => {\n if (classList.contains(\"vuuFilterClauseColumn\")) {\n return \"column\";\n } else if (classList.contains(\"vuuFilterClauseOperator\")) {\n return \"operator\";\n } else if (classList.contains(\"vuuFilterClauseValue\")) {\n return \"value\";\n } else {\n throw Error(\n \"getFilterClauseField, filterClauseElemnent is missing required class name\"\n );\n }\n};\n\nexport const getFocusedFieldDetails = (): [number, string] | [] => {\n const el = document.activeElement as HTMLElement;\n const field = queryClosest(el, \".vuuFilterClauseField\");\n const filterClause = queryClosest(field, \".vuuFilterClause\");\n if (filterClause && field) {\n return [getElementDataIndex(filterClause), getFilterClauseDetails(field)];\n } else {\n return [];\n }\n};\n\n// Focus the input control within field. If clause passed, will\n// focus first field within clause\nconst focusField = (fieldOrClause: HTMLElement | null) => {\n const input = fieldOrClause?.querySelector(\"input\");\n if (input) {\n input.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n }\n};\n\nexport const elementIsFilterCombinator = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClauseCombinator\");\n\nexport const elementIsFilterClause = (\n element: Element | null\n): element is HTMLElement =>\n element !== null && element.classList.contains(\"vuuFilterClause\");\n\nexport const focusFilterClauseField = (\n filterEditor: HTMLElement,\n filterClauseIndex: number,\n fieldName: FilterClauseFieldName = \"value\"\n) => {\n if (filterEditor) {\n const fieldClassName = mapFilterFieldToClassName[fieldName];\n const field = filterEditor.querySelector(\n `.vuuFilterClause[data-index=\"${filterClauseIndex}\"] .${fieldClassName}`\n ) as HTMLElement;\n focusField(field);\n }\n};\n\nexport const focusLastClauseValue = (filterEditor: HTMLElement) => {\n console.log(\"focusLastClauseValue\");\n const field = Array.from(\n filterEditor?.querySelectorAll(\".vuuFilterClauseField\")\n ).at(-1) as HTMLElement;\n focusField(field);\n};\n\nexport const focusNextFocusableElement = (direction: \"fwd\" | \"bwd\" = \"fwd\") => {\n const activeField = getFocusedField() as HTMLElement;\n const filterClause = getFilterClauseElement(activeField);\n if (direction === \"fwd\" && filterClause?.lastChild === activeField) {\n requestAnimationFrame(() => {\n focusNextFocusableElement();\n });\n } else {\n const nextField =\n direction === \"fwd\"\n ? (activeField?.nextElementSibling as HTMLElement)\n : (activeField?.previousElementSibling as HTMLElement);\n\n nextField?.querySelector(\"input\")?.focus();\n }\n};\n\nconst getFocusedField = () => {\n const activeElement = document.activeElement as HTMLElement;\n if (activeElement?.classList.contains(\"vuuFilterClause-clearButton\")) {\n return activeElement as HTMLElement;\n } else {\n return getFilterClauseFieldElement(activeElement);\n }\n};\n\nexport const focusNextElement = () => {\n const filterClauseField = getFocusedField();\n const filterClause = getFilterClauseElement(filterClauseField);\n if (filterClause && filterClauseField) {\n if (filterClauseField.classList.contains(\"vuuFilterClauseValue\")) {\n const clearButton = filterClause.querySelector(\n \".vuuFilterClause-clearButton\"\n ) as HTMLButtonElement;\n clearButton?.focus();\n } else {\n focusNextFocusableElement();\n }\n }\n};\n\nconst cursorAtTextStart = (input: HTMLInputElement) =>\n input.selectionStart === 0;\n\nconst cursorAtTextEnd = (input: HTMLInputElement) =>\n input.selectionStart === input.value.length;\n\nconst getFieldName = (field: HTMLElement) =>\n field?.classList.contains(\"vuuFilterClauseColumn\")\n ? \"column\"\n : field?.classList.contains(\"vuuFilterClauseOperator\")\n ? \"operator\"\n : \"value\";\n\nexport const clauseIsNotFirst = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n if (clause) {\n const index = getElementDataIndex(clause);\n return index > 0;\n }\n};\n\nconst clauseIsNotLast = (el: HTMLElement) => {\n const clause = getFilterClauseElement(el);\n const nextClause = clause?.nextElementSibling as HTMLElement;\n return nextClause?.classList.contains(\"vuuFilterClauseCombinator\");\n};\n\nexport const tabToPreviousFilterCombinator = (currentElement: HTMLElement) => {\n const filterClause = getFilterClauseElement(currentElement);\n const nextItem = filterClause.previousSibling as HTMLElement;\n console.log(`tab to previous combinator`);\n nextItem?.focus();\n};\n\nexport const navigateToNextFilterClause = (\n currentElement: HTMLElement,\n direction: \"bwd\" | \"fwd\" = \"fwd\"\n) => {\n if (direction === \"bwd\") {\n if (elementIsFilterCombinator(currentElement)) {\n const nextClause = currentElement.previousElementSibling;\n if (elementIsFilterClause(nextClause)) {\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n console.log(`focus field Value ${nextField?.classList}`);\n focusField(nextField);\n }\n } else {\n const filterClause = getFilterClauseElement(currentElement);\n const nextClause = filterClause.previousSibling as HTMLElement;\n const nextField = nextClause.querySelector(\n \".vuuFilterClauseValue\"\n ) as HTMLElement;\n focusField(nextField);\n }\n } else {\n const nextClause = currentElement.nextSibling as HTMLElement;\n focusField(nextClause);\n }\n};\n\n// The logic around preventDefault/stopPropagation is important\n// in this function\nexport const navigateToNextItemIfAtBoundary = (\n evt: KeyboardEvent<HTMLInputElement>\n) => {\n const input = evt.target as HTMLInputElement;\n const field = getFilterClauseFieldElement(input);\n if (evt.key === \"ArrowLeft\") {\n if (cursorAtTextStart(input)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"column\") {\n if (clauseIsNotFirst(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.previousElementSibling as HTMLElement;\n combinator?.focus();\n }\n } else {\n evt.preventDefault();\n focusField(field.previousSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at start. We want the arrowLeft to move the cursor\n evt.stopPropagation();\n } else if (evt.key === \"ArrowRight\") {\n if (cursorAtTextEnd(input as HTMLInputElement)) {\n const fieldName = getFieldName(field);\n if (fieldName === \"value\") {\n if (clauseIsNotLast(input)) {\n const filterClause = getFilterClauseElement(field);\n const combinator = filterClause.nextElementSibling as HTMLElement;\n combinator?.focus();\n }\n // Do not preventDefault, stopPropagation\n return;\n } else {\n evt.preventDefault();\n focusField(field.nextSibling as HTMLElement);\n }\n }\n // stopPropagation, even if cursor is not at end. We want the arrowRight to move the cursor\n evt.stopPropagation();\n }\n};\n\nexport const focusFirstClauseIfAllClausesValid = (\n filterEditor: HTMLElement\n) => {\n const columInput = Array.from(\n filterEditor.querySelectorAll(\".vuuFilterClauseColumn input\")\n ) as HTMLInputElement[];\n if (columInput.every((input) => input.value.length > 0)) {\n setTimeout(() => {\n const input = columInput.at(0);\n input?.select();\n input?.focus();\n }, 100);\n }\n};\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { hasOpenOptionList } from '@vuu-ui/vuu-utils';
2
2
  import { useState, useMemo, useRef, useCallback, useEffect } from 'react';
3
- import { clauseIsNotFirst, focusNextFocusableElement, focusNextElement, navigateToNextItemIfAtBoundary, tabToPreviousFilterCombinator, elementIsFilterClause, focusField } from './filterClauseFocusManagement.js';
3
+ import { clauseIsNotFirst, focusNextFocusableElement, focusNextElement, navigateToNextItemIfAtBoundary, tabToPreviousFilterCombinator } from './filterClauseFocusManagement.js';
4
4
 
5
5
  const useFilterClause = ({
6
6
  filterClauseModel,
@@ -18,6 +18,16 @@ const useFilterClause = ({
18
18
  }, [filterClauseModel]);
19
19
  const columnRef = useRef(null);
20
20
  const operatorRef = useRef(null);
21
+ const valueRef = useRef(null);
22
+ const setValueRef = useCallback(
23
+ (el) => {
24
+ valueRef.current = el;
25
+ if (!filterClauseModel.isValid) {
26
+ el?.querySelector("input")?.focus();
27
+ }
28
+ },
29
+ [filterClauseModel.isValid]
30
+ );
21
31
  const removeAndNavigateToNextInputIfAtBoundary = useCallback(
22
32
  (evt) => {
23
33
  const input = evt.target;
@@ -107,11 +117,6 @@ const useFilterClause = ({
107
117
  removeAndNavigateToNextInputIfAtBoundary
108
118
  ]
109
119
  );
110
- const handleFocus = useCallback((evt) => {
111
- if (elementIsFilterClause(evt.target)) {
112
- focusField(evt.target);
113
- }
114
- }, []);
115
120
  const inputProps = useMemo(
116
121
  () => ({
117
122
  onKeyDownCapture: handleKeyDownCaptureNavigation,
@@ -120,10 +125,10 @@ const useFilterClause = ({
120
125
  [handleKeyDownCaptureNavigation]
121
126
  );
122
127
  useEffect(() => {
123
- if (filterClauseModel.column === void 0) {
128
+ if (!filterClauseModel.isValid) {
129
+ const inputRef = filterClauseModel.column === void 0 ? columnRef : filterClauseModel.op === void 0 ? operatorRef : null;
124
130
  requestAnimationFrame(() => {
125
- const columnInput = columnRef?.current?.querySelector("input");
126
- columnInput?.focus();
131
+ inputRef?.current?.querySelector("input")?.focus();
127
132
  });
128
133
  }
129
134
  }, [filterClauseModel]);
@@ -134,10 +139,10 @@ const useFilterClause = ({
134
139
  onChangeValue: handleChangeValue,
135
140
  onDeselectValue: handleDeselectValue,
136
141
  onSelectColumn,
137
- onFocus: handleFocus,
138
142
  onSelectOperator,
139
143
  operatorRef,
140
- selectedColumn: columnsByName[filterClause.column ?? ""]
144
+ selectedColumn: columnsByName[filterClauseModel.column ?? ""],
145
+ valueRef: setValueRef
141
146
  };
142
147
  };
143
148
 
@@ -1 +1 @@
1
- {"version":3,"file":"useFilterClause.js","sources":["../../src/filter-clause/useFilterClause.ts"],"sourcesContent":["import { FilterClause, FilterClauseOp } from \"@vuu-ui/vuu-filter-types\";\nimport { hasOpenOptionList } from \"@vuu-ui/vuu-utils\";\nimport {\n FocusEventHandler,\n KeyboardEvent,\n SyntheticEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FilterClauseProps } from \"./FilterClause\";\nimport {\n clauseIsNotFirst,\n elementIsFilterClause,\n focusField,\n focusNextElement,\n focusNextFocusableElement,\n navigateToNextItemIfAtBoundary,\n tabToPreviousFilterCombinator,\n} from \"./filterClauseFocusManagement\";\nexport type FilterClauseEditorHookProps = Pick<\n FilterClauseProps,\n \"columnsByName\" | \"filterClauseModel\" | \"onCancel\" | \"onFocusSave\"\n>;\n\nexport type FilterClauseValueChangeHandler = (\n value: string | string[] | number | number[],\n isFinal?: boolean\n) => void;\n\nexport const useFilterClause = ({\n filterClauseModel,\n onCancel,\n columnsByName,\n onFocusSave,\n}: FilterClauseEditorHookProps) => {\n const [filterClause, setFilterClause] = useState<Partial<FilterClause>>(\n filterClauseModel.isValid ? filterClauseModel.asFilter() : {}\n );\n\n useMemo(() => {\n filterClauseModel.on(\"filterClause\", (filterClause) => {\n setFilterClause(filterClause);\n });\n }, [filterClauseModel]);\n\n const columnRef = useRef<HTMLDivElement>(null);\n const operatorRef = useRef<HTMLDivElement>(null);\n\n const removeAndNavigateToNextInputIfAtBoundary = useCallback(\n (evt: KeyboardEvent) => {\n const input = evt.target as HTMLInputElement;\n if (input.value === \"\") {\n const field = input.closest(\"[data-field]\") as HTMLElement;\n switch (field?.dataset?.field) {\n case \"operator\": {\n filterClauseModel.column = undefined;\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"value\": {\n filterClauseModel.setOp(undefined);\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"column\": {\n if (clauseIsNotFirst(input)) {\n // When we backspace from an empty clause, the clause will be removed.filterClause\n // In this case, we will reposition focus on previous clause, but we\n // don't want the backspace to be effect an edit on that clause.\n evt.preventDefault();\n onCancel?.(filterClauseModel, \"Backspace\");\n }\n }\n }\n }\n },\n [filterClauseModel, onCancel]\n );\n\n const onSelectColumn = (evt: SyntheticEvent, selectedColumn: string) => {\n if (selectedColumn) {\n if (evt?.type === \"keydown\") {\n const { key } = evt as KeyboardEvent;\n if (key === \"Tab\") {\n if (filterClauseModel.column === selectedColumn) {\n // No selection change, allow normal Tab navigation (to Save button)\n return;\n } else {\n // Tab is being used to change selection, keep focus within the clause\n evt.preventDefault();\n }\n }\n }\n }\n filterClauseModel.column = selectedColumn;\n setTimeout(() => {\n console.log(`focus next element`);\n focusNextElement();\n }, 100);\n };\n\n const onSelectOperator = useCallback(\n (_, selectedOp: FilterClauseOp) => {\n filterClauseModel.setOp(selectedOp);\n focusNextElement();\n },\n [filterClauseModel]\n );\n\n const handleChangeValue = useCallback<FilterClauseValueChangeHandler>(\n (value, isFinal) => filterClauseModel.setValue(value, isFinal),\n [filterClauseModel]\n );\n\n const handleDeselectValue = useCallback(\n () => filterClauseModel.setValue(undefined),\n [filterClauseModel]\n );\n\n const handleKeyDownCaptureNavigation = useCallback(\n (evt: KeyboardEvent<HTMLInputElement>) => {\n if ([\"ArrowLeft\", \"ArrowRight\"].includes(evt.key)) {\n navigateToNextItemIfAtBoundary(evt);\n } else if (evt.key === \"Backspace\") {\n removeAndNavigateToNextInputIfAtBoundary(evt);\n } else if (evt.key === \"Escape\") {\n // ignore when optionlist is open, the optionList will be collapsed\n if (!hasOpenOptionList(evt.target)) {\n onCancel?.(filterClauseModel, \"Escape\");\n }\n } else if (evt.key === \"Tab\" && evt.shiftKey) {\n evt.preventDefault();\n tabToPreviousFilterCombinator(evt.target as HTMLElement);\n } else if (evt.key === \"Tab\") {\n // if the clause is valid, skip to save\n if (filterClauseModel.isValid) {\n evt.preventDefault();\n evt.stopPropagation();\n // TODO focus cancel if not changed\n onFocusSave?.();\n }\n }\n },\n [\n filterClauseModel,\n onCancel,\n onFocusSave,\n removeAndNavigateToNextInputIfAtBoundary,\n ]\n );\n\n const handleFocus = useCallback<FocusEventHandler>((evt) => {\n if (elementIsFilterClause(evt.target)) {\n focusField(evt.target);\n }\n }, []);\n\n const inputProps = useMemo(\n () => ({\n onKeyDownCapture: handleKeyDownCaptureNavigation,\n tabIndex: -1,\n }),\n [handleKeyDownCaptureNavigation]\n );\n\n // Do we need this or can we leave it to the filterEditor\n useEffect(() => {\n if (filterClauseModel.column === undefined) {\n requestAnimationFrame(() => {\n const columnInput = columnRef?.current?.querySelector(\"input\");\n columnInput?.focus();\n });\n }\n }, [filterClauseModel]);\n\n return {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue: handleChangeValue,\n onDeselectValue: handleDeselectValue,\n onSelectColumn,\n onFocus: handleFocus,\n onSelectOperator,\n operatorRef,\n selectedColumn: columnsByName[filterClause.column ?? \"\"],\n };\n};\n"],"names":["filterClause"],"mappings":"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"}
1
+ {"version":3,"file":"useFilterClause.js","sources":["../../src/filter-clause/useFilterClause.ts"],"sourcesContent":["import { FilterClause, FilterClauseOp } from \"@vuu-ui/vuu-filter-types\";\nimport { hasOpenOptionList } from \"@vuu-ui/vuu-utils\";\nimport {\n KeyboardEvent,\n RefCallback,\n SyntheticEvent,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FilterClauseProps } from \"./FilterClause\";\nimport {\n clauseIsNotFirst,\n focusNextElement,\n focusNextFocusableElement,\n navigateToNextItemIfAtBoundary,\n tabToPreviousFilterCombinator,\n} from \"./filterClauseFocusManagement\";\nexport type FilterClauseEditorHookProps = Pick<\n FilterClauseProps,\n \"columnsByName\" | \"filterClauseModel\" | \"onCancel\" | \"onFocusSave\"\n>;\n\nexport type FilterClauseValueChangeHandler = (\n value: string | string[] | number | number[],\n isFinal?: boolean\n) => void;\n\nexport const useFilterClause = ({\n filterClauseModel,\n onCancel,\n columnsByName,\n onFocusSave,\n}: FilterClauseEditorHookProps) => {\n const [filterClause, setFilterClause] = useState<Partial<FilterClause>>(\n filterClauseModel.isValid ? filterClauseModel.asFilter() : {}\n );\n\n useMemo(() => {\n filterClauseModel.on(\"filterClause\", (filterClause) => {\n setFilterClause(filterClause);\n });\n }, [filterClauseModel]);\n\n const columnRef = useRef<HTMLDivElement>(null);\n const operatorRef = useRef<HTMLDivElement>(null);\n const valueRef = useRef<HTMLDivElement | null>(null);\n\n const setValueRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n valueRef.current = el;\n if (!filterClauseModel.isValid) {\n el?.querySelector(\"input\")?.focus();\n }\n },\n [filterClauseModel.isValid]\n );\n\n const removeAndNavigateToNextInputIfAtBoundary = useCallback(\n (evt: KeyboardEvent) => {\n const input = evt.target as HTMLInputElement;\n if (input.value === \"\") {\n const field = input.closest(\"[data-field]\") as HTMLElement;\n switch (field?.dataset?.field) {\n case \"operator\": {\n filterClauseModel.column = undefined;\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"value\": {\n filterClauseModel.setOp(undefined);\n focusNextFocusableElement(\"bwd\");\n break;\n }\n case \"column\": {\n if (clauseIsNotFirst(input)) {\n // When we backspace from an empty clause, the clause will be removed.filterClause\n // In this case, we will reposition focus on previous clause, but we\n // don't want the backspace to be effect an edit on that clause.\n evt.preventDefault();\n onCancel?.(filterClauseModel, \"Backspace\");\n }\n }\n }\n }\n },\n [filterClauseModel, onCancel]\n );\n\n const onSelectColumn = (evt: SyntheticEvent, selectedColumn: string) => {\n if (selectedColumn) {\n if (evt?.type === \"keydown\") {\n const { key } = evt as KeyboardEvent;\n if (key === \"Tab\") {\n if (filterClauseModel.column === selectedColumn) {\n // No selection change, allow normal Tab navigation (to Save button)\n return;\n } else {\n // Tab is being used to change selection, keep focus within the clause\n evt.preventDefault();\n }\n }\n }\n }\n filterClauseModel.column = selectedColumn;\n setTimeout(() => {\n console.log(`focus next element`);\n focusNextElement();\n }, 100);\n };\n\n const onSelectOperator = useCallback(\n (_, selectedOp: FilterClauseOp) => {\n filterClauseModel.setOp(selectedOp);\n focusNextElement();\n },\n [filterClauseModel]\n );\n\n const handleChangeValue = useCallback<FilterClauseValueChangeHandler>(\n (value, isFinal) => filterClauseModel.setValue(value, isFinal),\n [filterClauseModel]\n );\n\n const handleDeselectValue = useCallback(\n () => filterClauseModel.setValue(undefined),\n [filterClauseModel]\n );\n\n const handleKeyDownCaptureNavigation = useCallback(\n (evt: KeyboardEvent<HTMLInputElement>) => {\n if ([\"ArrowLeft\", \"ArrowRight\"].includes(evt.key)) {\n navigateToNextItemIfAtBoundary(evt);\n } else if (evt.key === \"Backspace\") {\n removeAndNavigateToNextInputIfAtBoundary(evt);\n } else if (evt.key === \"Escape\") {\n // ignore when optionlist is open, the optionList will be collapsed\n if (!hasOpenOptionList(evt.target)) {\n onCancel?.(filterClauseModel, \"Escape\");\n }\n } else if (evt.key === \"Tab\" && evt.shiftKey) {\n evt.preventDefault();\n tabToPreviousFilterCombinator(evt.target as HTMLElement);\n } else if (evt.key === \"Tab\") {\n // if the clause is valid, skip to save\n if (filterClauseModel.isValid) {\n evt.preventDefault();\n evt.stopPropagation();\n // TODO focus cancel if not changed\n onFocusSave?.();\n }\n }\n },\n [\n filterClauseModel,\n onCancel,\n onFocusSave,\n removeAndNavigateToNextInputIfAtBoundary,\n ]\n );\n\n const inputProps = useMemo(\n () => ({\n onKeyDownCapture: handleKeyDownCaptureNavigation,\n tabIndex: -1,\n }),\n [handleKeyDownCaptureNavigation]\n );\n\n // Do we need this or can we leave it to the filterEditor\n useEffect(() => {\n // leave the valueInput to callbackRef handler above, may\n // fire after the requestAnimationFrame\n if (!filterClauseModel.isValid) {\n const inputRef =\n filterClauseModel.column === undefined\n ? columnRef\n : filterClauseModel.op === undefined\n ? operatorRef\n : null;\n\n requestAnimationFrame(() => {\n inputRef?.current?.querySelector(\"input\")?.focus();\n });\n }\n }, [filterClauseModel]);\n\n return {\n inputProps,\n columnRef,\n filterClause,\n onChangeValue: handleChangeValue,\n onDeselectValue: handleDeselectValue,\n onSelectColumn,\n onSelectOperator,\n operatorRef,\n selectedColumn: columnsByName[filterClauseModel.column ?? \"\"],\n valueRef: setValueRef,\n };\n};\n"],"names":["filterClause"],"mappings":"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"}
@@ -3,72 +3,77 @@ import cx from 'clsx';
3
3
  import { FilterClauseValueEditorNumber } from './FilterClauseValueEditorNumber.js';
4
4
  import { FilterClauseValueEditorText } from './FilterClauseValueEditorText.js';
5
5
  import { isDateTimeColumn } from '@vuu-ui/vuu-utils';
6
+ import { forwardRef } from 'react';
6
7
  import { FilterClauseValueEditorDate } from './FilterClauseValueEditorDate.js';
7
8
 
8
9
  const classBase = "vuuFilterClause";
9
- const FilterClauseValueEditor = ({
10
- selectedColumn,
11
- operator,
12
- inputProps,
13
- onChangeValue,
14
- onDeselectValue,
15
- suggestionProvider,
16
- table,
17
- value
18
- }) => {
19
- if (selectedColumn === void 0 || operator === void 0) {
20
- return null;
21
- }
22
- if (isDateTimeColumn(selectedColumn)) {
23
- console.log(`return DateInput`);
24
- return /* @__PURE__ */ jsx(
25
- FilterClauseValueEditorDate,
26
- {
27
- inputProps,
28
- className: cx(`${classBase}Field`, `${classBase}Value`),
29
- "data-field": "value",
30
- value,
31
- operator,
32
- onChangeValue
33
- }
34
- );
35
- }
36
- switch (selectedColumn.serverDataType) {
37
- case "string":
38
- case "char":
39
- return /* @__PURE__ */ jsx(
40
- FilterClauseValueEditorText,
41
- {
42
- inputProps,
43
- className: cx(`${classBase}Field`, `${classBase}Value`),
44
- column: selectedColumn,
45
- onDeselect: onDeselectValue,
46
- onChangeValue,
47
- operator,
48
- suggestionProvider,
49
- table,
50
- value: value === void 0 ? "" : Array.isArray(value) ? value.map((val) => val.toString()) : value.toString()
51
- }
52
- );
53
- case "int":
54
- case "long":
55
- case "double":
10
+ const FilterClauseValueEditor = forwardRef(
11
+ function FilterClauseValueEditor2({
12
+ selectedColumn,
13
+ operator,
14
+ inputProps,
15
+ onChangeValue,
16
+ onDeselectValue,
17
+ suggestionProvider,
18
+ table,
19
+ value
20
+ }, forwardedRef) {
21
+ if (selectedColumn === void 0 || operator === void 0) {
22
+ return null;
23
+ }
24
+ if (isDateTimeColumn(selectedColumn)) {
25
+ console.log(`return DateInput`);
56
26
  return /* @__PURE__ */ jsx(
57
- FilterClauseValueEditorNumber,
27
+ FilterClauseValueEditorDate,
58
28
  {
59
29
  inputProps,
60
30
  className: cx(`${classBase}Field`, `${classBase}Value`),
61
- column: selectedColumn,
62
31
  "data-field": "value",
63
- onChangeValue,
64
- operator
32
+ value,
33
+ operator,
34
+ onChangeValue
65
35
  }
66
36
  );
67
- default:
68
- console.log("returning null");
69
- return null;
37
+ }
38
+ switch (selectedColumn.serverDataType) {
39
+ case "string":
40
+ case "char":
41
+ return /* @__PURE__ */ jsx(
42
+ FilterClauseValueEditorText,
43
+ {
44
+ inputProps,
45
+ className: cx(`${classBase}Field`, `${classBase}Value`),
46
+ column: selectedColumn,
47
+ onDeselect: onDeselectValue,
48
+ onChangeValue,
49
+ operator,
50
+ ref: forwardedRef,
51
+ suggestionProvider,
52
+ table,
53
+ value: value === void 0 ? "" : Array.isArray(value) ? value.map((val) => val.toString()) : value.toString()
54
+ }
55
+ );
56
+ case "int":
57
+ case "long":
58
+ case "double":
59
+ return /* @__PURE__ */ jsx(
60
+ FilterClauseValueEditorNumber,
61
+ {
62
+ inputProps,
63
+ className: cx(`${classBase}Field`, `${classBase}Value`),
64
+ column: selectedColumn,
65
+ "data-field": "value",
66
+ onChangeValue,
67
+ operator,
68
+ ref: forwardedRef
69
+ }
70
+ );
71
+ default:
72
+ console.log("returning null");
73
+ return null;
74
+ }
70
75
  }
71
- };
76
+ );
72
77
 
73
78
  export { FilterClauseValueEditor };
74
79
  //# sourceMappingURL=FilterClauseValueEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilterClauseValueEditor.js","sources":["../../../src/filter-clause/value-editors/FilterClauseValueEditor.tsx"],"sourcesContent":["import { TableSchemaTable } from \"@vuu-ui/vuu-data-types\";\nimport cx from \"clsx\";\nimport { FilterClauseValueEditorNumber } from \"./FilterClauseValueEditorNumber\";\nimport { FilterClauseValueEditorText } from \"./FilterClauseValueEditorText\";\nimport { useFilterClause } from \"../useFilterClause\";\n\nimport {\n NumericFilterClauseOp,\n SingleValueFilterClauseOp,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { isDateTimeColumn } from \"@vuu-ui/vuu-utils\";\nimport { FilterClauseValueEditorDate } from \"./FilterClauseValueEditorDate\";\nimport { FilterClauseProps } from \"../FilterClause\";\n\nconst classBase = \"vuuFilterClause\";\n\ntype FilterClauseValueEditorProps = Pick<\n ReturnType<typeof useFilterClause>,\n \"selectedColumn\" | \"inputProps\" | \"onChangeValue\" | \"onDeselectValue\"\n> &\n Pick<FilterClauseProps, \"suggestionProvider\"> & {\n table?: TableSchemaTable;\n } & {\n operator?: SingleValueFilterClauseOp | \"in\";\n value?: string | string[] | number | number[] | boolean | boolean[];\n };\n\nexport const FilterClauseValueEditor: React.FC<\n FilterClauseValueEditorProps\n> = ({\n selectedColumn,\n operator,\n inputProps,\n onChangeValue,\n onDeselectValue,\n suggestionProvider,\n table,\n value,\n}) => {\n if (selectedColumn === undefined || operator === undefined) {\n return null;\n }\n\n if (isDateTimeColumn(selectedColumn)) {\n console.log(`return DateInput`);\n return (\n <FilterClauseValueEditorDate\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n data-field=\"value\"\n value={value as number}\n operator={operator as NumericFilterClauseOp}\n onChangeValue={onChangeValue}\n />\n );\n }\n\n switch (selectedColumn.serverDataType) {\n case \"string\":\n case \"char\":\n return (\n <FilterClauseValueEditorText\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n onDeselect={onDeselectValue}\n onChangeValue={onChangeValue}\n operator={operator}\n suggestionProvider={suggestionProvider}\n table={table}\n value={\n value === undefined\n ? \"\"\n : Array.isArray(value)\n ? value.map((val) => val.toString())\n : (value.toString() as string | string[])\n }\n />\n );\n case \"int\":\n case \"long\":\n case \"double\":\n return (\n <FilterClauseValueEditorNumber\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n data-field=\"value\"\n onChangeValue={onChangeValue}\n operator={operator}\n />\n );\n default:\n console.log(\"returning null\");\n return null;\n }\n};\n"],"names":[],"mappings":";;;;;;;AAcA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAaX,MAAM,0BAET,CAAC;AAAA,EACH,cAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,kBAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AACF,CAAM,KAAA;AACJ,EAAI,IAAA,cAAA,KAAmB,KAAa,CAAA,IAAA,QAAA,KAAa,KAAW,CAAA,EAAA;AAC1D,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAI,IAAA,gBAAA,CAAiB,cAAc,CAAG,EAAA;AACpC,IAAA,OAAA,CAAQ,IAAI,CAAkB,gBAAA,CAAA,CAAA,CAAA;AAC9B,IACE,uBAAA,GAAA;AAAA,MAAC,2BAAA;AAAA,MAAA;AAAA,QACC,UAAA;AAAA,QACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,QACtD,YAAW,EAAA,OAAA;AAAA,QACX,KAAA;AAAA,QACA,QAAA;AAAA,QACA,aAAA;AAAA,OAAA;AAAA,KACF,CAAA;AAAA,GAEJ;AAEA,EAAA,QAAQ,eAAe,cAAgB;AAAA,IACrC,KAAK,QAAA,CAAA;AAAA,IACL,KAAK,MAAA;AACH,MACE,uBAAA,GAAA;AAAA,QAAC,2BAAA;AAAA,QAAA;AAAA,UACC,UAAA;AAAA,UACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,UACtD,MAAQ,EAAA,cAAA;AAAA,UACR,UAAY,EAAA,eAAA;AAAA,UACZ,aAAA;AAAA,UACA,QAAA;AAAA,UACA,kBAAA;AAAA,UACA,KAAA;AAAA,UACA,OACE,KAAU,KAAA,KAAA,CAAA,GACN,EACA,GAAA,KAAA,CAAM,QAAQ,KAAK,CAAA,GACnB,KAAM,CAAA,GAAA,CAAI,CAAC,GAAQ,KAAA,GAAA,CAAI,UAAU,CAAA,GAChC,MAAM,QAAS,EAAA;AAAA,SAAA;AAAA,OAExB,CAAA;AAAA,IAEJ,KAAK,KAAA,CAAA;AAAA,IACL,KAAK,MAAA,CAAA;AAAA,IACL,KAAK,QAAA;AACH,MACE,uBAAA,GAAA;AAAA,QAAC,6BAAA;AAAA,QAAA;AAAA,UACC,UAAA;AAAA,UACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,UACtD,MAAQ,EAAA,cAAA;AAAA,UACR,YAAW,EAAA,OAAA;AAAA,UACX,aAAA;AAAA,UACA,QAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,IAEJ;AACE,MAAA,OAAA,CAAQ,IAAI,gBAAgB,CAAA,CAAA;AAC5B,MAAO,OAAA,IAAA,CAAA;AAAA,GACX;AACF;;;;"}
1
+ {"version":3,"file":"FilterClauseValueEditor.js","sources":["../../../src/filter-clause/value-editors/FilterClauseValueEditor.tsx"],"sourcesContent":["import { TableSchemaTable } from \"@vuu-ui/vuu-data-types\";\nimport cx from \"clsx\";\nimport { useFilterClause } from \"../useFilterClause\";\nimport { FilterClauseValueEditorNumber } from \"./FilterClauseValueEditorNumber\";\nimport { FilterClauseValueEditorText } from \"./FilterClauseValueEditorText\";\n\nimport {\n NumericFilterClauseOp,\n SingleValueFilterClauseOp,\n} from \"@vuu-ui/vuu-filter-types\";\nimport { isDateTimeColumn } from \"@vuu-ui/vuu-utils\";\nimport { ForwardedRef, forwardRef } from \"react\";\nimport { FilterClauseProps } from \"../FilterClause\";\nimport { FilterClauseValueEditorDate } from \"./FilterClauseValueEditorDate\";\n\nconst classBase = \"vuuFilterClause\";\n\ntype FilterClauseValueEditorProps = Pick<\n ReturnType<typeof useFilterClause>,\n \"selectedColumn\" | \"inputProps\" | \"onChangeValue\" | \"onDeselectValue\"\n> &\n Pick<FilterClauseProps, \"suggestionProvider\"> & {\n table?: TableSchemaTable;\n } & {\n operator?: SingleValueFilterClauseOp | \"in\";\n value?: string | string[] | number | number[] | boolean | boolean[];\n };\n\nexport const FilterClauseValueEditor = forwardRef(\n function FilterClauseValueEditor(\n {\n selectedColumn,\n operator,\n inputProps,\n onChangeValue,\n onDeselectValue,\n suggestionProvider,\n table,\n value,\n }: FilterClauseValueEditorProps,\n forwardedRef: ForwardedRef<HTMLDivElement>\n ) {\n if (selectedColumn === undefined || operator === undefined) {\n return null;\n }\n\n if (isDateTimeColumn(selectedColumn)) {\n console.log(`return DateInput`);\n return (\n <FilterClauseValueEditorDate\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n data-field=\"value\"\n // ref={forwardedRef}\n value={value as number}\n operator={operator as NumericFilterClauseOp}\n onChangeValue={onChangeValue}\n />\n );\n }\n\n switch (selectedColumn.serverDataType) {\n case \"string\":\n case \"char\":\n return (\n <FilterClauseValueEditorText\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n onDeselect={onDeselectValue}\n onChangeValue={onChangeValue}\n operator={operator}\n ref={forwardedRef}\n suggestionProvider={suggestionProvider}\n table={table}\n value={\n value === undefined\n ? \"\"\n : Array.isArray(value)\n ? value.map((val) => val.toString())\n : (value.toString() as string | string[])\n }\n />\n );\n case \"int\":\n case \"long\":\n case \"double\":\n return (\n <FilterClauseValueEditorNumber\n inputProps={inputProps}\n className={cx(`${classBase}Field`, `${classBase}Value`)}\n column={selectedColumn}\n data-field=\"value\"\n onChangeValue={onChangeValue}\n operator={operator}\n ref={forwardedRef}\n />\n );\n default:\n console.log(\"returning null\");\n return null;\n }\n }\n);\n"],"names":["FilterClauseValueEditor"],"mappings":";;;;;;;;AAeA,MAAM,SAAY,GAAA,iBAAA,CAAA;AAaX,MAAM,uBAA0B,GAAA,UAAA;AAAA,EACrC,SAASA,wBACP,CAAA;AAAA,IACE,cAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA,aAAA;AAAA,IACA,eAAA;AAAA,IACA,kBAAA;AAAA,IACA,KAAA;AAAA,IACA,KAAA;AAAA,KAEF,YACA,EAAA;AACA,IAAI,IAAA,cAAA,KAAmB,KAAa,CAAA,IAAA,QAAA,KAAa,KAAW,CAAA,EAAA;AAC1D,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AAEA,IAAI,IAAA,gBAAA,CAAiB,cAAc,CAAG,EAAA;AACpC,MAAA,OAAA,CAAQ,IAAI,CAAkB,gBAAA,CAAA,CAAA,CAAA;AAC9B,MACE,uBAAA,GAAA;AAAA,QAAC,2BAAA;AAAA,QAAA;AAAA,UACC,UAAA;AAAA,UACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,UACtD,YAAW,EAAA,OAAA;AAAA,UAEX,KAAA;AAAA,UACA,QAAA;AAAA,UACA,aAAA;AAAA,SAAA;AAAA,OACF,CAAA;AAAA,KAEJ;AAEA,IAAA,QAAQ,eAAe,cAAgB;AAAA,MACrC,KAAK,QAAA,CAAA;AAAA,MACL,KAAK,MAAA;AACH,QACE,uBAAA,GAAA;AAAA,UAAC,2BAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,YACtD,MAAQ,EAAA,cAAA;AAAA,YACR,UAAY,EAAA,eAAA;AAAA,YACZ,aAAA;AAAA,YACA,QAAA;AAAA,YACA,GAAK,EAAA,YAAA;AAAA,YACL,kBAAA;AAAA,YACA,KAAA;AAAA,YACA,OACE,KAAU,KAAA,KAAA,CAAA,GACN,EACA,GAAA,KAAA,CAAM,QAAQ,KAAK,CAAA,GACnB,KAAM,CAAA,GAAA,CAAI,CAAC,GAAQ,KAAA,GAAA,CAAI,UAAU,CAAA,GAChC,MAAM,QAAS,EAAA;AAAA,WAAA;AAAA,SAExB,CAAA;AAAA,MAEJ,KAAK,KAAA,CAAA;AAAA,MACL,KAAK,MAAA,CAAA;AAAA,MACL,KAAK,QAAA;AACH,QACE,uBAAA,GAAA;AAAA,UAAC,6BAAA;AAAA,UAAA;AAAA,YACC,UAAA;AAAA,YACA,WAAW,EAAG,CAAA,CAAA,EAAG,SAAS,CAAS,KAAA,CAAA,EAAA,CAAA,EAAG,SAAS,CAAO,KAAA,CAAA,CAAA;AAAA,YACtD,MAAQ,EAAA,cAAA;AAAA,YACR,YAAW,EAAA,OAAA;AAAA,YACX,aAAA;AAAA,YACA,QAAA;AAAA,YACA,GAAK,EAAA,YAAA;AAAA,WAAA;AAAA,SACP,CAAA;AAAA,MAEJ;AACE,QAAA,OAAA,CAAQ,IAAI,gBAAgB,CAAA,CAAA;AAC5B,QAAO,OAAA,IAAA,CAAA;AAAA,KACX;AAAA,GACF;AACF;;;;"}
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
- "version": "0.8.72",
2
+ "version": "0.8.73",
3
3
  "author": "heswell",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "devDependencies": {
7
- "@vuu-ui/vuu-data-types": "0.8.72",
8
- "@vuu-ui/vuu-protocol-types": "0.8.72",
9
- "@vuu-ui/vuu-table-types": "0.8.72",
10
- "@vuu-ui/vuu-filter-types": "0.8.72",
7
+ "@vuu-ui/vuu-data-types": "0.8.73",
8
+ "@vuu-ui/vuu-protocol-types": "0.8.73",
9
+ "@vuu-ui/vuu-table-types": "0.8.73",
10
+ "@vuu-ui/vuu-filter-types": "0.8.73",
11
11
  "@types/uuid": "^9.0.2"
12
12
  },
13
13
  "dependencies": {
14
- "@vuu-ui/vuu-data-react": "0.8.72",
15
- "@vuu-ui/vuu-filter-parser": "0.8.72",
16
- "@vuu-ui/vuu-popups": "0.8.72",
17
- "@vuu-ui/vuu-ui-controls": "0.8.72",
18
- "@vuu-ui/vuu-table": "0.8.72",
19
- "@vuu-ui/vuu-utils": "0.8.72",
14
+ "@vuu-ui/vuu-data-react": "0.8.73",
15
+ "@vuu-ui/vuu-filter-parser": "0.8.73",
16
+ "@vuu-ui/vuu-popups": "0.8.73",
17
+ "@vuu-ui/vuu-ui-controls": "0.8.73",
18
+ "@vuu-ui/vuu-table": "0.8.73",
19
+ "@vuu-ui/vuu-utils": "0.8.73",
20
20
  "@salt-ds/core": "1.27.1",
21
21
  "@salt-ds/styles": "0.2.1",
22
22
  "@salt-ds/window": "0.1.1",
@@ -1,7 +1,6 @@
1
1
  import { KeyboardEvent } from "react";
2
2
  type FilterClauseFieldName = "column" | "operator" | "value";
3
3
  export declare const getFocusedFieldDetails: () => [number, string] | [];
4
- export declare const focusField: (fieldOrClause: HTMLElement | null) => void;
5
4
  export declare const elementIsFilterCombinator: (element: Element | null) => element is HTMLElement;
6
5
  export declare const elementIsFilterClause: (element: Element | null) => element is HTMLElement;
7
6
  export declare const focusFilterClauseField: (filterEditor: HTMLElement, filterClauseIndex: number, fieldName?: FilterClauseFieldName) => void;
@@ -1,5 +1,5 @@
1
1
  import { FilterClause, FilterClauseOp } from "@vuu-ui/vuu-filter-types";
2
- import { FocusEventHandler, KeyboardEvent, SyntheticEvent } from "react";
2
+ import { KeyboardEvent, RefCallback, SyntheticEvent } from "react";
3
3
  import { FilterClauseProps } from "./FilterClause";
4
4
  export type FilterClauseEditorHookProps = Pick<FilterClauseProps, "columnsByName" | "filterClauseModel" | "onCancel" | "onFocusSave">;
5
5
  export type FilterClauseValueChangeHandler = (value: string | string[] | number | number[], isFinal?: boolean) => void;
@@ -13,8 +13,8 @@ export declare const useFilterClause: ({ filterClauseModel, onCancel, columnsByN
13
13
  onChangeValue: FilterClauseValueChangeHandler;
14
14
  onDeselectValue: () => void;
15
15
  onSelectColumn: (evt: SyntheticEvent, selectedColumn: string) => void;
16
- onFocus: FocusEventHandler;
17
16
  onSelectOperator: (_: any, selectedOp: FilterClauseOp) => void;
18
17
  operatorRef: import("react").RefObject<HTMLDivElement>;
19
18
  selectedColumn: import("packages/vuu-table-types").ColumnDescriptor;
19
+ valueRef: RefCallback<HTMLDivElement>;
20
20
  };
@@ -1,13 +1,24 @@
1
1
  /// <reference types="react" />
2
2
  import { TableSchemaTable } from "@vuu-ui/vuu-data-types";
3
- import { useFilterClause } from "../useFilterClause";
4
3
  import { SingleValueFilterClauseOp } from "@vuu-ui/vuu-filter-types";
5
4
  import { FilterClauseProps } from "../FilterClause";
6
- type FilterClauseValueEditorProps = Pick<ReturnType<typeof useFilterClause>, "selectedColumn" | "inputProps" | "onChangeValue" | "onDeselectValue"> & Pick<FilterClauseProps, "suggestionProvider"> & {
7
- table?: TableSchemaTable;
5
+ export declare const FilterClauseValueEditor: import("react").ForwardRefExoticComponent<Pick<{
6
+ inputProps: {
7
+ onKeyDownCapture: (evt: import("react").KeyboardEvent<HTMLInputElement>) => void;
8
+ tabIndex: number;
9
+ };
10
+ columnRef: import("react").RefObject<HTMLDivElement>;
11
+ filterClause: Partial<import("@vuu-ui/vuu-filter-types").FilterClause>;
12
+ onChangeValue: import("../useFilterClause").FilterClauseValueChangeHandler;
13
+ onDeselectValue: () => void;
14
+ onSelectColumn: (evt: import("react").SyntheticEvent<Element, Event>, selectedColumn: string) => void;
15
+ onSelectOperator: (_: any, selectedOp: import("@vuu-ui/vuu-filter-types").FilterClauseOp) => void;
16
+ operatorRef: import("react").RefObject<HTMLDivElement>;
17
+ selectedColumn: import("packages/vuu-table-types").ColumnDescriptor;
18
+ valueRef: import("react").RefCallback<HTMLDivElement>;
19
+ }, "inputProps" | "onChangeValue" | "selectedColumn" | "onDeselectValue"> & Pick<FilterClauseProps, "suggestionProvider"> & {
20
+ table?: TableSchemaTable | undefined;
8
21
  } & {
9
- operator?: SingleValueFilterClauseOp | "in";
10
- value?: string | string[] | number | number[] | boolean | boolean[];
11
- };
12
- export declare const FilterClauseValueEditor: React.FC<FilterClauseValueEditorProps>;
13
- export {};
22
+ operator?: "in" | SingleValueFilterClauseOp | undefined;
23
+ value?: string | number | boolean | string[] | number[] | boolean[] | undefined;
24
+ } & import("react").RefAttributes<HTMLDivElement>>;