@vuu-ui/vuu-filters 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +0 -0
- package/cjs/FilterModel.js +233 -0
- package/cjs/FilterModel.js.map +1 -0
- package/cjs/filter-bar/FilterBar.css.js +6 -0
- package/cjs/filter-bar/FilterBar.css.js.map +1 -0
- package/cjs/filter-bar/FilterBar.js +134 -0
- package/cjs/filter-bar/FilterBar.js.map +1 -0
- package/cjs/filter-bar/FilterBarMenu.js +12 -0
- package/cjs/filter-bar/FilterBarMenu.js.map +1 -0
- package/cjs/filter-bar/filterBarFocusManagement.js +35 -0
- package/cjs/filter-bar/filterBarFocusManagement.js.map +1 -0
- package/cjs/filter-bar/useApplyFilterOnChange.js +33 -0
- package/cjs/filter-bar/useApplyFilterOnChange.js.map +1 -0
- package/cjs/filter-bar/useFilterBar.js +250 -0
- package/cjs/filter-bar/useFilterBar.js.map +1 -0
- package/cjs/filter-bar/useFilterState.js +150 -0
- package/cjs/filter-bar/useFilterState.js.map +1 -0
- package/cjs/filter-clause/ExpandoCombobox.css.js +6 -0
- package/cjs/filter-clause/ExpandoCombobox.css.js.map +1 -0
- package/cjs/filter-clause/ExpandoCombobox.js +148 -0
- package/cjs/filter-clause/ExpandoCombobox.js.map +1 -0
- package/cjs/filter-clause/FilterClause.css.js +6 -0
- package/cjs/filter-clause/FilterClause.css.js.map +1 -0
- package/cjs/filter-clause/FilterClause.js +112 -0
- package/cjs/filter-clause/FilterClause.js.map +1 -0
- package/cjs/filter-clause/filterClauseFocusManagement.js +203 -0
- package/cjs/filter-clause/filterClauseFocusManagement.js.map +1 -0
- package/cjs/filter-clause/operator-utils.js +20 -0
- package/cjs/filter-clause/operator-utils.js.map +1 -0
- package/cjs/filter-clause/useFilterClause.js +142 -0
- package/cjs/filter-clause/useFilterClause.js.map +1 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditor.js +77 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditor.js.map +1 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditorDate.js +75 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditorDate.js.map +1 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditorNumber.js +55 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditorNumber.js.map +1 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditorText.js +193 -0
- package/cjs/filter-clause/value-editors/FilterClauseValueEditorText.js.map +1 -0
- package/cjs/filter-editor/FilterClauseCombinator.css.js +6 -0
- package/cjs/filter-editor/FilterClauseCombinator.css.js.map +1 -0
- package/cjs/filter-editor/FilterClauseCombinator.js +42 -0
- package/cjs/filter-editor/FilterClauseCombinator.js.map +1 -0
- package/cjs/filter-editor/FilterEditor.css.js +6 -0
- package/cjs/filter-editor/FilterEditor.css.js.map +1 -0
- package/cjs/filter-editor/FilterEditor.js +97 -0
- package/cjs/filter-editor/FilterEditor.js.map +1 -0
- package/cjs/filter-editor/useFilterEditor.js +188 -0
- package/cjs/filter-editor/useFilterEditor.js.map +1 -0
- package/cjs/filter-input/FilterInput.css.js +6 -0
- package/cjs/filter-input/FilterInput.css.js.map +1 -0
- package/cjs/filter-input/FilterInput.js +52 -0
- package/cjs/filter-input/FilterInput.js.map +1 -0
- package/cjs/filter-input/FilterLanguage.js +24 -0
- package/cjs/filter-input/FilterLanguage.js.map +1 -0
- package/cjs/filter-input/filterInfo.js +17 -0
- package/cjs/filter-input/filterInfo.js.map +1 -0
- package/cjs/filter-input/highlighting.js +12 -0
- package/cjs/filter-input/highlighting.js.map +1 -0
- package/cjs/filter-input/theme.js +79 -0
- package/cjs/filter-input/theme.js.map +1 -0
- package/cjs/filter-input/useCodeMirrorEditor.js +136 -0
- package/cjs/filter-input/useCodeMirrorEditor.js.map +1 -0
- package/cjs/filter-input/useFilterAutoComplete.js +243 -0
- package/cjs/filter-input/useFilterAutoComplete.js.map +1 -0
- package/cjs/filter-input/useFilterSuggestionProvider.js +206 -0
- package/cjs/filter-input/useFilterSuggestionProvider.js.map +1 -0
- package/cjs/filter-pill/FilterPill.css.js +6 -0
- package/cjs/filter-pill/FilterPill.css.js.map +1 -0
- package/cjs/filter-pill/FilterPill.js +147 -0
- package/cjs/filter-pill/FilterPill.js.map +1 -0
- package/cjs/filter-pill/FilterPillMenuOptions.js +32 -0
- package/cjs/filter-pill/FilterPillMenuOptions.js.map +1 -0
- package/cjs/filter-pill/filterAsReactNode.js +27 -0
- package/cjs/filter-pill/filterAsReactNode.js.map +1 -0
- package/cjs/filter-pill/getFilterLabel.js +22 -0
- package/cjs/filter-pill/getFilterLabel.js.map +1 -0
- package/cjs/filter-pill/getFilterTooltipText.js +41 -0
- package/cjs/filter-pill/getFilterTooltipText.js.map +1 -0
- package/cjs/filter-utils.js +328 -0
- package/cjs/filter-utils.js.map +1 -0
- package/cjs/index.js +50 -0
- package/cjs/index.js.map +1 -0
- package/cjs/use-filter-config.js +39 -0
- package/cjs/use-filter-config.js.map +1 -0
- package/cjs/use-rest-config.js +63 -0
- package/cjs/use-rest-config.js.map +1 -0
- package/esm/FilterModel.js +230 -0
- package/esm/FilterModel.js.map +1 -0
- package/esm/filter-bar/FilterBar.css.js +4 -0
- package/esm/filter-bar/FilterBar.css.js.map +1 -0
- package/esm/filter-bar/FilterBar.js +132 -0
- package/esm/filter-bar/FilterBar.js.map +1 -0
- package/esm/filter-bar/FilterBarMenu.js +10 -0
- package/esm/filter-bar/FilterBarMenu.js.map +1 -0
- package/esm/filter-bar/filterBarFocusManagement.js +33 -0
- package/esm/filter-bar/filterBarFocusManagement.js.map +1 -0
- package/esm/filter-bar/useApplyFilterOnChange.js +31 -0
- package/esm/filter-bar/useApplyFilterOnChange.js.map +1 -0
- package/esm/filter-bar/useFilterBar.js +248 -0
- package/esm/filter-bar/useFilterBar.js.map +1 -0
- package/esm/filter-bar/useFilterState.js +148 -0
- package/esm/filter-bar/useFilterState.js.map +1 -0
- package/esm/filter-clause/ExpandoCombobox.css.js +4 -0
- package/esm/filter-clause/ExpandoCombobox.css.js.map +1 -0
- package/esm/filter-clause/ExpandoCombobox.js +146 -0
- package/esm/filter-clause/ExpandoCombobox.js.map +1 -0
- package/esm/filter-clause/FilterClause.css.js +4 -0
- package/esm/filter-clause/FilterClause.css.js.map +1 -0
- package/esm/filter-clause/FilterClause.js +110 -0
- package/esm/filter-clause/FilterClause.js.map +1 -0
- package/esm/filter-clause/filterClauseFocusManagement.js +189 -0
- package/esm/filter-clause/filterClauseFocusManagement.js.map +1 -0
- package/esm/filter-clause/operator-utils.js +16 -0
- package/esm/filter-clause/operator-utils.js.map +1 -0
- package/esm/filter-clause/useFilterClause.js +140 -0
- package/esm/filter-clause/useFilterClause.js.map +1 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditor.js +75 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditor.js.map +1 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditorDate.js +73 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditorDate.js.map +1 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditorNumber.js +53 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditorNumber.js.map +1 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditorText.js +191 -0
- package/esm/filter-clause/value-editors/FilterClauseValueEditorText.js.map +1 -0
- package/esm/filter-editor/FilterClauseCombinator.css.js +4 -0
- package/esm/filter-editor/FilterClauseCombinator.css.js.map +1 -0
- package/esm/filter-editor/FilterClauseCombinator.js +40 -0
- package/esm/filter-editor/FilterClauseCombinator.js.map +1 -0
- package/esm/filter-editor/FilterEditor.css.js +4 -0
- package/esm/filter-editor/FilterEditor.css.js.map +1 -0
- package/esm/filter-editor/FilterEditor.js +95 -0
- package/esm/filter-editor/FilterEditor.js.map +1 -0
- package/esm/filter-editor/useFilterEditor.js +186 -0
- package/esm/filter-editor/useFilterEditor.js.map +1 -0
- package/esm/filter-input/FilterInput.css.js +4 -0
- package/esm/filter-input/FilterInput.css.js.map +1 -0
- package/esm/filter-input/FilterInput.js +50 -0
- package/esm/filter-input/FilterInput.js.map +1 -0
- package/esm/filter-input/FilterLanguage.js +22 -0
- package/esm/filter-input/FilterLanguage.js.map +1 -0
- package/esm/filter-input/filterInfo.js +15 -0
- package/esm/filter-input/filterInfo.js.map +1 -0
- package/esm/filter-input/highlighting.js +10 -0
- package/esm/filter-input/highlighting.js.map +1 -0
- package/esm/filter-input/theme.js +77 -0
- package/esm/filter-input/theme.js.map +1 -0
- package/esm/filter-input/useCodeMirrorEditor.js +134 -0
- package/esm/filter-input/useCodeMirrorEditor.js.map +1 -0
- package/esm/filter-input/useFilterAutoComplete.js +241 -0
- package/esm/filter-input/useFilterAutoComplete.js.map +1 -0
- package/esm/filter-input/useFilterSuggestionProvider.js +204 -0
- package/esm/filter-input/useFilterSuggestionProvider.js.map +1 -0
- package/esm/filter-pill/FilterPill.css.js +4 -0
- package/esm/filter-pill/FilterPill.css.js.map +1 -0
- package/esm/filter-pill/FilterPill.js +145 -0
- package/esm/filter-pill/FilterPill.js.map +1 -0
- package/esm/filter-pill/FilterPillMenuOptions.js +27 -0
- package/esm/filter-pill/FilterPillMenuOptions.js.map +1 -0
- package/esm/filter-pill/filterAsReactNode.js +25 -0
- package/esm/filter-pill/filterAsReactNode.js.map +1 -0
- package/esm/filter-pill/getFilterLabel.js +20 -0
- package/esm/filter-pill/getFilterLabel.js.map +1 -0
- package/esm/filter-pill/getFilterTooltipText.js +39 -0
- package/esm/filter-pill/getFilterTooltipText.js.map +1 -0
- package/esm/filter-utils.js +307 -0
- package/esm/filter-utils.js.map +1 -0
- package/esm/index.js +13 -0
- package/esm/index.js.map +1 -0
- package/esm/use-filter-config.js +37 -0
- package/esm/use-filter-config.js.map +1 -0
- package/esm/use-rest-config.js +61 -0
- package/esm/use-rest-config.js.map +1 -0
- package/package.json +48 -0
- package/types/FilterModel.d.ts +43 -0
- package/types/column-filter/utils.d.ts +4 -0
- package/types/filter-bar/FilterBar.d.ts +22 -0
- package/types/filter-bar/FilterBarMenu.d.ts +2 -0
- package/types/filter-bar/filterBarFocusManagement.d.ts +1 -0
- package/types/filter-bar/index.d.ts +1 -0
- package/types/filter-bar/useApplyFilterOnChange.d.ts +10 -0
- package/types/filter-bar/useFilterBar.d.ts +30 -0
- package/types/filter-bar/useFilterState.d.ts +17 -0
- package/types/filter-clause/ExpandoCombobox.d.ts +10 -0
- package/types/filter-clause/FilterClause.d.ts +17 -0
- package/types/filter-clause/FilterMenu.d.ts +10 -0
- package/types/filter-clause/FilterMenuOptions.d.ts +6 -0
- package/types/filter-clause/filterClauseFocusManagement.d.ts +16 -0
- package/types/filter-clause/filterClauseTypes.d.ts +13 -0
- package/types/filter-clause/index.d.ts +3 -0
- package/types/filter-clause/operator-utils.d.ts +4 -0
- package/types/filter-clause/useFilterClause.d.ts +24 -0
- package/types/filter-clause/value-editors/FilterClauseValueEditor.d.ts +13 -0
- package/types/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +10 -0
- package/types/filter-clause/value-editors/FilterClauseValueEditorNumber.d.ts +10 -0
- package/types/filter-clause/value-editors/FilterClauseValueEditorText.d.ts +11 -0
- package/types/filter-editor/FilterClauseCombinator.d.ts +9 -0
- package/types/filter-editor/FilterEditor.d.ts +16 -0
- package/types/filter-editor/index.d.ts +1 -0
- package/types/filter-editor/useFilterEditor.d.ts +31 -0
- package/types/filter-input/FilterInput.d.ts +10 -0
- package/types/filter-input/FilterLanguage.d.ts +2 -0
- package/types/filter-input/filterInfo.d.ts +1 -0
- package/types/filter-input/highlighting.d.ts +1 -0
- package/types/filter-input/index.d.ts +3 -0
- package/types/filter-input/theme.d.ts +1 -0
- package/types/filter-input/useCodeMirrorEditor.d.ts +35 -0
- package/types/filter-input/useFilterAutoComplete.d.ts +9 -0
- package/types/filter-input/useFilterSuggestionProvider.d.ts +16 -0
- package/types/filter-pill/FilterPill.d.ts +17 -0
- package/types/filter-pill/FilterPillMenuOptions.d.ts +12 -0
- package/types/filter-pill/filterAsReactNode.d.ts +3 -0
- package/types/filter-pill/getFilterLabel.d.ts +2 -0
- package/types/filter-pill/getFilterTooltipText.d.ts +2 -0
- package/types/filter-pill/index.d.ts +1 -0
- package/types/filter-pill-menu/FilterPillMenu.d.ts +18 -0
- package/types/filter-pill-menu/FilterPillMenuOptions.d.ts +12 -0
- package/types/filter-pill-menu/index.d.ts +1 -0
- package/types/filter-utils.d.ts +48 -0
- package/types/index.d.ts +8 -0
- package/types/use-filter-config.d.ts +12 -0
- package/types/use-rest-config.d.ts +11 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vuuCodemirror = require('@vuu-ui/vuu-codemirror');
|
|
4
|
+
var vuuFilterParser = require('@vuu-ui/vuu-filter-parser');
|
|
5
|
+
var cx = require('clsx');
|
|
6
|
+
var react = require('react');
|
|
7
|
+
var FilterLanguage = require('./FilterLanguage.js');
|
|
8
|
+
var highlighting = require('./highlighting.js');
|
|
9
|
+
var theme = require('./theme.js');
|
|
10
|
+
var useFilterAutoComplete = require('./useFilterAutoComplete.js');
|
|
11
|
+
|
|
12
|
+
const getView = (ref) => {
|
|
13
|
+
if (ref.current == void 0) {
|
|
14
|
+
throw Error("EditorView not defined");
|
|
15
|
+
}
|
|
16
|
+
return ref.current;
|
|
17
|
+
};
|
|
18
|
+
const getOptionClass = (completion) => {
|
|
19
|
+
return cx("vuuSuggestion", {
|
|
20
|
+
vuuIllustration: completion.isIllustration
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const stripName = (filterQuery) => {
|
|
24
|
+
const pos = filterQuery.lastIndexOf(" as ");
|
|
25
|
+
if (pos !== -1) {
|
|
26
|
+
return filterQuery.slice(0, pos);
|
|
27
|
+
} else {
|
|
28
|
+
return filterQuery;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const noop = () => console.log("noooop");
|
|
32
|
+
const useCodeMirrorEditor = ({
|
|
33
|
+
existingFilter,
|
|
34
|
+
onSubmitFilter,
|
|
35
|
+
suggestionProvider
|
|
36
|
+
}) => {
|
|
37
|
+
const editorRef = react.useRef(null);
|
|
38
|
+
const onSubmit = react.useRef(noop);
|
|
39
|
+
const viewRef = react.useRef();
|
|
40
|
+
const completionFn = useFilterAutoComplete.useAutoComplete(
|
|
41
|
+
suggestionProvider,
|
|
42
|
+
onSubmit,
|
|
43
|
+
existingFilter
|
|
44
|
+
);
|
|
45
|
+
const [createState, clearInput] = react.useMemo(() => {
|
|
46
|
+
const parseFilter = () => {
|
|
47
|
+
const view = getView(viewRef);
|
|
48
|
+
const source = view.state.doc.toString();
|
|
49
|
+
const tree = vuuCodemirror.ensureSyntaxTree(view.state, view.state.doc.length, 5e3);
|
|
50
|
+
if (tree) {
|
|
51
|
+
const filter = vuuFilterParser.walkTree(tree, source);
|
|
52
|
+
return [filter, stripName(source), filter.name];
|
|
53
|
+
} else {
|
|
54
|
+
return [void 0, "", void 0];
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const clearInput2 = () => {
|
|
58
|
+
getView(viewRef).setState(createState2());
|
|
59
|
+
};
|
|
60
|
+
const submitFilterAndClearInput = (mode) => {
|
|
61
|
+
const [filter, filterQuery, filterName] = parseFilter();
|
|
62
|
+
onSubmitFilter?.(filter, filterQuery, mode, filterName);
|
|
63
|
+
clearInput2();
|
|
64
|
+
};
|
|
65
|
+
const submitFilter = (key) => {
|
|
66
|
+
return vuuCodemirror.keymap.of([
|
|
67
|
+
{
|
|
68
|
+
key,
|
|
69
|
+
run() {
|
|
70
|
+
submitFilterAndClearInput();
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]);
|
|
75
|
+
};
|
|
76
|
+
const showSuggestions = (key) => {
|
|
77
|
+
return vuuCodemirror.keymap.of([
|
|
78
|
+
{
|
|
79
|
+
key,
|
|
80
|
+
run() {
|
|
81
|
+
vuuCodemirror.startCompletion(getView(viewRef));
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]);
|
|
86
|
+
};
|
|
87
|
+
const createState2 = () => vuuCodemirror.EditorState.create({
|
|
88
|
+
doc: "",
|
|
89
|
+
extensions: [
|
|
90
|
+
vuuCodemirror.minimalSetup,
|
|
91
|
+
vuuCodemirror.autocompletion({
|
|
92
|
+
override: [completionFn],
|
|
93
|
+
optionClass: getOptionClass
|
|
94
|
+
}),
|
|
95
|
+
FilterLanguage.filterLanguageSupport(),
|
|
96
|
+
vuuCodemirror.keymap.of(vuuCodemirror.defaultKeymap),
|
|
97
|
+
submitFilter("Ctrl-Enter"),
|
|
98
|
+
showSuggestions("ArrowDown"),
|
|
99
|
+
vuuCodemirror.EditorView.updateListener.of((v) => {
|
|
100
|
+
const view = getView(viewRef);
|
|
101
|
+
if (v.docChanged) {
|
|
102
|
+
vuuCodemirror.startCompletion(view);
|
|
103
|
+
}
|
|
104
|
+
}),
|
|
105
|
+
vuuCodemirror.EditorState.transactionFilter.of(
|
|
106
|
+
(tr) => tr.newDoc.lines > 1 ? [] : tr
|
|
107
|
+
),
|
|
108
|
+
theme.vuuTheme,
|
|
109
|
+
highlighting.vuuHighlighting
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
onSubmit.current = (mode) => {
|
|
113
|
+
submitFilterAndClearInput(mode);
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
getView(viewRef).focus();
|
|
116
|
+
}, 100);
|
|
117
|
+
};
|
|
118
|
+
return [createState2, clearInput2];
|
|
119
|
+
}, [completionFn, onSubmitFilter]);
|
|
120
|
+
react.useEffect(() => {
|
|
121
|
+
if (!editorRef.current) {
|
|
122
|
+
throw Error("editor not in dom");
|
|
123
|
+
}
|
|
124
|
+
viewRef.current = new vuuCodemirror.EditorView({
|
|
125
|
+
state: createState(),
|
|
126
|
+
parent: editorRef.current
|
|
127
|
+
});
|
|
128
|
+
return () => {
|
|
129
|
+
viewRef.current?.destroy();
|
|
130
|
+
};
|
|
131
|
+
}, [completionFn, createState]);
|
|
132
|
+
return { editorRef, clearInput };
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
exports.useCodeMirrorEditor = useCodeMirrorEditor;
|
|
136
|
+
//# sourceMappingURL=useCodeMirrorEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCodeMirrorEditor.js","sources":["../../src/filter-input/useCodeMirrorEditor.ts"],"sourcesContent":["import {\n autocompletion,\n Completion,\n defaultKeymap,\n EditorState,\n EditorView,\n ensureSyntaxTree,\n keymap,\n minimalSetup,\n startCompletion,\n VuuCompletion,\n} from \"@vuu-ui/vuu-codemirror\";\nimport { walkTree } from \"@vuu-ui/vuu-filter-parser\";\nimport { Filter } from \"@vuu-ui/vuu-filter-types\";\nimport cx from \"clsx\";\nimport { MutableRefObject, useEffect, useMemo, useRef } from \"react\";\nimport { filterLanguageSupport } from \"./FilterLanguage\";\nimport { vuuHighlighting } from \"./highlighting\";\nimport { vuuTheme } from \"./theme\";\nimport {\n ApplyCompletion,\n FilterSubmissionMode,\n useAutoComplete,\n} from \"./useFilterAutoComplete\";\nimport { FilterSaveOptions } from \"./useFilterSuggestionProvider\";\n\nexport type SuggestionType =\n | \"column\"\n | \"columnValue\"\n | \"operator\"\n | \"save\"\n | \"name\";\n\nexport interface FilterSuggestionOptions {\n quoted?: boolean;\n columnName?: string;\n existingFilter?: Filter;\n filterName?: string;\n onSubmit?: () => void;\n operator?: string;\n startsWith?: string;\n selection?: string[];\n}\n\nexport type getFilterSuggestionsType = (\n suggestionType: SuggestionType,\n options?: FilterSuggestionOptions\n) => Promise<Completion[]>;\n\nexport interface IFilterSuggestionProvider {\n getSuggestions: getFilterSuggestionsType;\n isPartialMatch: (\n valueType: SuggestionType,\n columnName?: string,\n text?: string | undefined\n ) => Promise<boolean>;\n}\n\nexport interface SuggestionConsumer {\n suggestionProvider: IFilterSuggestionProvider;\n}\n\nconst getView = (ref: MutableRefObject<EditorView | undefined>): EditorView => {\n if (ref.current == undefined) {\n throw Error(\"EditorView not defined\");\n }\n return ref.current;\n};\n\nconst getOptionClass = (completion: VuuCompletion) => {\n return cx(\"vuuSuggestion\", {\n vuuIllustration: completion.isIllustration,\n });\n};\n\nconst stripName = (filterQuery: string) => {\n const pos = filterQuery.lastIndexOf(\" as \");\n if (pos !== -1) {\n return filterQuery.slice(0, pos);\n } else {\n return filterQuery;\n }\n};\n\nconst noop = () => console.log(\"noooop\");\n\nexport type filterSubmissionHandler = (\n filter: Filter | undefined,\n filterQuery: string,\n mode?: FilterSubmissionMode,\n filterName?: string\n) => void;\n\nexport interface CodeMirrorEditorProps {\n existingFilter?: Filter;\n onSubmitFilter?: filterSubmissionHandler;\n saveOptions?: FilterSaveOptions;\n suggestionProvider: IFilterSuggestionProvider;\n}\n\nexport const useCodeMirrorEditor = ({\n existingFilter,\n onSubmitFilter,\n suggestionProvider,\n}: CodeMirrorEditorProps) => {\n const editorRef = useRef<HTMLDivElement>(null);\n const onSubmit = useRef<ApplyCompletion>(noop);\n const viewRef = useRef<EditorView>();\n const completionFn = useAutoComplete(\n suggestionProvider,\n onSubmit,\n existingFilter\n );\n\n const [createState, clearInput] = useMemo(() => {\n const parseFilter = ():\n | [Filter, string, string | undefined]\n | [undefined, \"\", undefined] => {\n const view = getView(viewRef);\n const source = view.state.doc.toString();\n const tree = ensureSyntaxTree(view.state, view.state.doc.length, 5000);\n if (tree) {\n const filter = walkTree(tree, source) as Filter;\n return [filter, stripName(source), filter.name];\n } else {\n return [undefined, \"\", undefined];\n }\n };\n\n const clearInput = () => {\n getView(viewRef).setState(createState());\n };\n\n const submitFilterAndClearInput = (mode?: FilterSubmissionMode) => {\n const [filter, filterQuery, filterName] = parseFilter();\n onSubmitFilter?.(filter, filterQuery, mode, filterName);\n clearInput();\n };\n\n const submitFilter = (key: string) => {\n return keymap.of([\n {\n key,\n run() {\n submitFilterAndClearInput();\n return true;\n },\n },\n ]);\n };\n\n const showSuggestions = (key: string) => {\n return keymap.of([\n {\n key,\n run() {\n startCompletion(getView(viewRef));\n return true;\n },\n },\n ]);\n };\n\n const createState = (): EditorState =>\n EditorState.create({\n doc: \"\",\n extensions: [\n minimalSetup,\n autocompletion({\n override: [completionFn],\n optionClass: getOptionClass,\n }),\n filterLanguageSupport(),\n keymap.of(defaultKeymap),\n submitFilter(\"Ctrl-Enter\"),\n showSuggestions(\"ArrowDown\"),\n EditorView.updateListener.of((v) => {\n const view = getView(viewRef);\n if (v.docChanged) {\n startCompletion(view);\n }\n }),\n EditorState.transactionFilter.of((tr) =>\n tr.newDoc.lines > 1 ? [] : tr\n ),\n vuuTheme,\n vuuHighlighting,\n ],\n });\n\n onSubmit.current = (mode?: FilterSubmissionMode) => {\n submitFilterAndClearInput(mode);\n // TODO refocu sthe editor\n setTimeout(() => {\n getView(viewRef).focus();\n }, 100);\n };\n\n return [createState, clearInput];\n }, [completionFn, onSubmitFilter]);\n\n useEffect(() => {\n if (!editorRef.current) {\n throw Error(\"editor not in dom\");\n }\n\n viewRef.current = new EditorView({\n state: createState(),\n parent: editorRef.current,\n });\n\n return () => {\n viewRef.current?.destroy();\n };\n }, [completionFn, createState]);\n\n return { editorRef, clearInput };\n};\n"],"names":["useRef","useAutoComplete","useMemo","ensureSyntaxTree","walkTree","clearInput","createState","keymap","startCompletion","EditorState","minimalSetup","autocompletion","filterLanguageSupport","defaultKeymap","EditorView","vuuTheme","vuuHighlighting","useEffect"],"mappings":"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"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vuuCodemirror = require('@vuu-ui/vuu-codemirror');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
const getOperator = (node, state) => {
|
|
7
|
+
let maybeColumnNode = node.prevSibling || node.parent;
|
|
8
|
+
while (maybeColumnNode && !["Column", "Operator", "In"].includes(maybeColumnNode.name)) {
|
|
9
|
+
maybeColumnNode = maybeColumnNode.prevSibling || maybeColumnNode.parent;
|
|
10
|
+
}
|
|
11
|
+
if (maybeColumnNode?.name === "In" || maybeColumnNode?.name === "Operator") {
|
|
12
|
+
return vuuCodemirror.getValue(maybeColumnNode, state);
|
|
13
|
+
} else {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const getPartialOperator = (maybeOperatorNode, state, columnName) => {
|
|
18
|
+
const value = vuuCodemirror.getValue(maybeOperatorNode, state);
|
|
19
|
+
if (columnName === void 0 || value === columnName) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (["contains", "ends", "starts"].some(
|
|
23
|
+
(val) => val.startsWith(value.toLowerCase())
|
|
24
|
+
)) {
|
|
25
|
+
return value;
|
|
26
|
+
} else {
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const getClauseOperator = (node, state) => {
|
|
31
|
+
let maybeTargetNode = node.prevSibling || node.parent || node.lastChild;
|
|
32
|
+
while (maybeTargetNode && maybeTargetNode.name === "\u26A0")
|
|
33
|
+
maybeTargetNode = maybeTargetNode.prevSibling;
|
|
34
|
+
if (maybeTargetNode && ["As", "Or", "And"].includes(maybeTargetNode.name)) {
|
|
35
|
+
return vuuCodemirror.getValue(maybeTargetNode, state);
|
|
36
|
+
} else {
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const getFilterName = (node, state) => {
|
|
41
|
+
if (node.name === "FilterName") {
|
|
42
|
+
return vuuCodemirror.getValue(node, state);
|
|
43
|
+
} else {
|
|
44
|
+
let maybeTargetNode = node.prevSibling || node.parent || node.lastChild;
|
|
45
|
+
while (maybeTargetNode && maybeTargetNode.name !== "FilterName")
|
|
46
|
+
maybeTargetNode = maybeTargetNode.prevSibling;
|
|
47
|
+
if (maybeTargetNode && maybeTargetNode.name === "FilterName") {
|
|
48
|
+
return vuuCodemirror.getValue(node, state);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const getColumnName = (node, state) => {
|
|
53
|
+
const prevNode = node.prevSibling;
|
|
54
|
+
if (prevNode?.name === "Column") {
|
|
55
|
+
return vuuCodemirror.getValue(prevNode, state);
|
|
56
|
+
} else if (prevNode?.name === "Operator") {
|
|
57
|
+
return getColumnName(prevNode, state);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const getSetValues = (node, state) => {
|
|
61
|
+
let maybeTargetNode = node.lastChild;
|
|
62
|
+
const values = [];
|
|
63
|
+
while (maybeTargetNode && maybeTargetNode.name !== "In") {
|
|
64
|
+
const value = vuuCodemirror.getValue(maybeTargetNode, state);
|
|
65
|
+
if (value) {
|
|
66
|
+
values.push(value);
|
|
67
|
+
} else {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
maybeTargetNode = maybeTargetNode.prevSibling;
|
|
71
|
+
}
|
|
72
|
+
return values;
|
|
73
|
+
};
|
|
74
|
+
const useAutoComplete = (suggestionProvider, onSubmit, existingFilter) => {
|
|
75
|
+
const makeSuggestions = react.useCallback(
|
|
76
|
+
async (context, suggestionType, optionalArgs = {}) => {
|
|
77
|
+
const { startsWith = "" } = optionalArgs;
|
|
78
|
+
const options = await suggestionProvider.getSuggestions(
|
|
79
|
+
suggestionType,
|
|
80
|
+
optionalArgs
|
|
81
|
+
);
|
|
82
|
+
return { from: context.pos - startsWith.length, options };
|
|
83
|
+
},
|
|
84
|
+
[suggestionProvider]
|
|
85
|
+
);
|
|
86
|
+
return react.useCallback(
|
|
87
|
+
async (context) => {
|
|
88
|
+
const { state, pos } = context;
|
|
89
|
+
const word = context.matchBefore(/\w*/) ?? {
|
|
90
|
+
from: 0,
|
|
91
|
+
to: 0,
|
|
92
|
+
text: void 0
|
|
93
|
+
};
|
|
94
|
+
const tree = vuuCodemirror.syntaxTree(state);
|
|
95
|
+
const nodeBefore = tree.resolveInner(pos, -1);
|
|
96
|
+
console.log({ nodeBeforeName: nodeBefore.name });
|
|
97
|
+
switch (nodeBefore.name) {
|
|
98
|
+
case "Filter":
|
|
99
|
+
if (context.pos === 0) {
|
|
100
|
+
return makeSuggestions(context, "column");
|
|
101
|
+
} else {
|
|
102
|
+
const clauseOperator = getClauseOperator(nodeBefore, state);
|
|
103
|
+
if (clauseOperator === "as") {
|
|
104
|
+
return makeSuggestions(context, "name");
|
|
105
|
+
} else {
|
|
106
|
+
const filterName = getFilterName(nodeBefore, state);
|
|
107
|
+
return makeSuggestions(context, "save", {
|
|
108
|
+
onSubmit: onSubmit.current,
|
|
109
|
+
existingFilter,
|
|
110
|
+
filterName
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
case "String":
|
|
115
|
+
{
|
|
116
|
+
const operator = getOperator(nodeBefore, state);
|
|
117
|
+
const columnName = getColumnName(nodeBefore, state);
|
|
118
|
+
const { from, to } = nodeBefore;
|
|
119
|
+
if (to - from === 2 && context.pos === from + 1) {
|
|
120
|
+
if (columnName && operator) {
|
|
121
|
+
return makeSuggestions(context, "columnValue", {
|
|
122
|
+
columnName,
|
|
123
|
+
operator,
|
|
124
|
+
quoted: true,
|
|
125
|
+
startsWith: word.text
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
console.log(
|
|
130
|
+
`we have a string, column is ${columnName} ${from} ${to}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
case "As":
|
|
136
|
+
return makeSuggestions(context, "name");
|
|
137
|
+
case "FilterName":
|
|
138
|
+
return makeSuggestions(context, "save", {
|
|
139
|
+
onSubmit: onSubmit.current,
|
|
140
|
+
existingFilter,
|
|
141
|
+
filterName: getFilterName(nodeBefore, state)
|
|
142
|
+
});
|
|
143
|
+
case "Column": {
|
|
144
|
+
const columnName = vuuCodemirror.getValue(nodeBefore, state);
|
|
145
|
+
const isPartialMatch = await suggestionProvider.isPartialMatch(
|
|
146
|
+
"column",
|
|
147
|
+
void 0,
|
|
148
|
+
columnName
|
|
149
|
+
);
|
|
150
|
+
if (isPartialMatch) {
|
|
151
|
+
return makeSuggestions(context, "column", {
|
|
152
|
+
startsWith: columnName
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
return makeSuggestions(context, "operator", { columnName });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
case "\u26A0": {
|
|
159
|
+
const columnName = vuuCodemirror.getNodeByName(nodeBefore, state);
|
|
160
|
+
const operator = getOperator(nodeBefore, state);
|
|
161
|
+
const partialOperator = operator ? void 0 : getPartialOperator(nodeBefore, state, columnName);
|
|
162
|
+
if (partialOperator) {
|
|
163
|
+
return makeSuggestions(context, "operator", {
|
|
164
|
+
columnName,
|
|
165
|
+
startsWith: partialOperator
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
return makeSuggestions(context, "columnValue", {
|
|
169
|
+
columnName,
|
|
170
|
+
operator,
|
|
171
|
+
startsWith: word.text
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
case "Identifier":
|
|
176
|
+
{
|
|
177
|
+
const clauseOperator = getClauseOperator(nodeBefore, state);
|
|
178
|
+
if (clauseOperator === "as") {
|
|
179
|
+
return {
|
|
180
|
+
from: context.pos,
|
|
181
|
+
options: [
|
|
182
|
+
{
|
|
183
|
+
label: "press ENTER to apply filter and save",
|
|
184
|
+
apply: () => onSubmit.current(),
|
|
185
|
+
boost: 5
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
case "ColumnSetExpression":
|
|
193
|
+
case "Values": {
|
|
194
|
+
const columnName = vuuCodemirror.getNodeByName(nodeBefore, state);
|
|
195
|
+
const selection = getSetValues(nodeBefore, state);
|
|
196
|
+
return makeSuggestions(context, "columnValue", {
|
|
197
|
+
columnName,
|
|
198
|
+
selection
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
case "Comma":
|
|
202
|
+
case "LBrack": {
|
|
203
|
+
const columnName = vuuCodemirror.getNodeByName(nodeBefore, state);
|
|
204
|
+
return makeSuggestions(context, "columnValue", { columnName });
|
|
205
|
+
}
|
|
206
|
+
case "ColumnValueExpression":
|
|
207
|
+
{
|
|
208
|
+
const lastToken = nodeBefore.lastChild?.prevSibling;
|
|
209
|
+
if (lastToken?.name === "Column") {
|
|
210
|
+
return makeSuggestions(context, "operator", {
|
|
211
|
+
columnName: vuuCodemirror.getNodeByName(nodeBefore, state)
|
|
212
|
+
});
|
|
213
|
+
} else if (lastToken?.name === "Operator") {
|
|
214
|
+
return makeSuggestions(context, "columnValue", {
|
|
215
|
+
columnName: vuuCodemirror.getNodeByName(lastToken, state),
|
|
216
|
+
operator: vuuCodemirror.getValue(lastToken, state)
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
case "In": {
|
|
222
|
+
return {
|
|
223
|
+
from: context.pos,
|
|
224
|
+
options: [{ label: "[", apply: " [", type: "text" }]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
case "Eq": {
|
|
228
|
+
return makeSuggestions(context, "columnValue", {
|
|
229
|
+
columnName: vuuCodemirror.getNodeByName(nodeBefore, state)
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
case "AndExpression":
|
|
233
|
+
case "OrExpression": {
|
|
234
|
+
return makeSuggestions(context, "column");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
[existingFilter, makeSuggestions, onSubmit, suggestionProvider]
|
|
239
|
+
);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
exports.useAutoComplete = useAutoComplete;
|
|
243
|
+
//# sourceMappingURL=useFilterAutoComplete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFilterAutoComplete.js","sources":["../../src/filter-input/useFilterAutoComplete.ts"],"sourcesContent":["import {\n CompletionContext,\n CompletionSource,\n EditorState,\n getNodeByName,\n getValue,\n SyntaxNode,\n syntaxTree,\n} from \"@vuu-ui/vuu-codemirror\";\nimport { Filter } from \"@vuu-ui/vuu-filter-types\";\nimport { MutableRefObject, useCallback } from \"react\";\nimport {\n IFilterSuggestionProvider,\n SuggestionType,\n} from \"./useCodeMirrorEditor\";\n\nexport type FilterSubmissionMode = \"and\" | \"or\" | \"replace\" | \"tab\";\n\nexport type ApplyCompletion = (mode?: FilterSubmissionMode) => void;\n\nconst getOperator = (node: SyntaxNode, state: EditorState) => {\n let maybeColumnNode = node.prevSibling || node.parent;\n while (\n maybeColumnNode &&\n ![\"Column\", \"Operator\", \"In\"].includes(maybeColumnNode.name)\n ) {\n maybeColumnNode = maybeColumnNode.prevSibling || maybeColumnNode.parent;\n }\n if (maybeColumnNode?.name === \"In\" || maybeColumnNode?.name === \"Operator\") {\n return getValue(maybeColumnNode, state);\n } else {\n return undefined;\n }\n};\n\n// Operators that are more than a single character in length may incur partial matches\n// TODO need to check that previous token is a column\nconst getPartialOperator = (\n maybeOperatorNode: SyntaxNode,\n state: EditorState,\n columnName?: string\n) => {\n const value = getValue(maybeOperatorNode, state);\n if (columnName === undefined || value === columnName) {\n return;\n }\n if (\n [\"contains\", \"ends\", \"starts\"].some((val) =>\n val.startsWith(value.toLowerCase())\n )\n ) {\n return value;\n } else {\n return undefined;\n }\n};\n\nconst getClauseOperator = (node: SyntaxNode, state: EditorState) => {\n let maybeTargetNode = node.prevSibling || node.parent || node.lastChild;\n while (maybeTargetNode && maybeTargetNode.name === \"⚠\")\n maybeTargetNode = maybeTargetNode.prevSibling;\n if (maybeTargetNode && [\"As\", \"Or\", \"And\"].includes(maybeTargetNode.name)) {\n return getValue(maybeTargetNode, state);\n } else {\n return undefined;\n }\n};\n\nconst getFilterName = (node: SyntaxNode, state: EditorState) => {\n if (node.name === \"FilterName\") {\n return getValue(node, state);\n } else {\n let maybeTargetNode = node.prevSibling || node.parent || node.lastChild;\n while (maybeTargetNode && maybeTargetNode.name !== \"FilterName\")\n maybeTargetNode = maybeTargetNode.prevSibling;\n if (maybeTargetNode && maybeTargetNode.name === \"FilterName\") {\n return getValue(node, state);\n }\n }\n};\n\nconst getColumnName = (\n node: SyntaxNode,\n state: EditorState\n): string | undefined => {\n const prevNode = node.prevSibling;\n if (prevNode?.name === \"Column\") {\n return getValue(prevNode, state);\n } else if (prevNode?.name === \"Operator\") {\n return getColumnName(prevNode, state);\n }\n};\n\nconst getSetValues = (node: SyntaxNode, state: EditorState): string[] => {\n let maybeTargetNode = node.lastChild;\n const values: string[] = [];\n while (maybeTargetNode && maybeTargetNode.name !== \"In\") {\n const value = getValue(maybeTargetNode, state);\n if (value) {\n values.push(value);\n } else {\n break;\n }\n maybeTargetNode = maybeTargetNode.prevSibling;\n }\n return values;\n};\nexport const FilterlNamedTerms: readonly string[] = [\n \"Filter\",\n \"ParenthesizedExpression\",\n \"AndExpression\",\n \"OrExpression\",\n \"ColumnValueExpression\",\n \"ColumnSetExpression\",\n \"FilterName\",\n \"Column\",\n \"Operator\",\n \"Values\",\n \"Number\",\n \"String\",\n];\nexport const lastNamedChild = (node: SyntaxNode): SyntaxNode | null => {\n let { lastChild } = node;\n while (lastChild && !FilterlNamedTerms.includes(lastChild.name)) {\n lastChild = lastChild.prevSibling;\n console.log(lastChild?.name);\n }\n return lastChild;\n};\n\nexport const useAutoComplete = (\n suggestionProvider: IFilterSuggestionProvider,\n onSubmit: MutableRefObject<ApplyCompletion>,\n existingFilter?: Filter\n) => {\n const makeSuggestions = useCallback(\n async (\n context: CompletionContext,\n suggestionType: SuggestionType,\n optionalArgs: {\n columnName?: string;\n existingFilter?: Filter;\n filterName?: string;\n operator?: string;\n quoted?: boolean;\n onSubmit?: () => void;\n selection?: string[];\n startsWith?: string;\n } = {}\n ) => {\n const { startsWith = \"\" } = optionalArgs;\n const options = await suggestionProvider.getSuggestions(\n suggestionType,\n optionalArgs\n );\n return { from: context.pos - startsWith.length, options };\n },\n [suggestionProvider]\n );\n\n return useCallback(\n async (context: CompletionContext) => {\n const { state, pos } = context;\n const word = context.matchBefore(/\\w*/) ?? {\n from: 0,\n to: 0,\n text: undefined,\n };\n\n const tree = syntaxTree(state);\n const nodeBefore = tree.resolveInner(pos, -1);\n console.log({ nodeBeforeName: nodeBefore.name });\n\n switch (nodeBefore.name) {\n case \"Filter\":\n if (context.pos === 0) {\n return makeSuggestions(context, \"column\");\n } else {\n const clauseOperator = getClauseOperator(nodeBefore, state);\n if (clauseOperator === \"as\") {\n return makeSuggestions(context, \"name\");\n } else {\n const filterName = getFilterName(nodeBefore, state);\n return makeSuggestions(context, \"save\", {\n onSubmit: onSubmit.current,\n existingFilter,\n filterName,\n });\n }\n }\n\n case \"String\":\n {\n // we only encounter a string as the right hand operand of a conditional expression\n const operator = getOperator(nodeBefore, state);\n const columnName = getColumnName(nodeBefore, state);\n // are we inside the string or immediately after it\n const { from, to } = nodeBefore;\n if (to - from === 2 && context.pos === from + 1) {\n // We are in an empty string, i.e between two quotes\n if (columnName && operator) {\n return makeSuggestions(context, \"columnValue\", {\n columnName,\n operator,\n quoted: true,\n startsWith: word.text,\n });\n }\n } else {\n console.log(\n `we have a string, column is ${columnName} ${from} ${to}`\n );\n }\n }\n break;\n\n case \"As\":\n return makeSuggestions(context, \"name\");\n\n case \"FilterName\":\n return makeSuggestions(context, \"save\", {\n onSubmit: onSubmit.current,\n existingFilter,\n filterName: getFilterName(nodeBefore, state),\n });\n\n case \"Column\": {\n const columnName = getValue(nodeBefore, state);\n const isPartialMatch = await suggestionProvider.isPartialMatch(\n \"column\",\n undefined,\n columnName\n );\n if (isPartialMatch) {\n return makeSuggestions(context, \"column\", {\n startsWith: columnName,\n });\n } else {\n return makeSuggestions(context, \"operator\", { columnName });\n }\n }\n\n case \"⚠\": {\n const columnName = getNodeByName(nodeBefore, state);\n const operator = getOperator(nodeBefore, state);\n // TODO check if we're mnatching a partial jojn operator\n const partialOperator = operator\n ? undefined\n : getPartialOperator(nodeBefore, state, columnName);\n\n if (partialOperator) {\n return makeSuggestions(context, \"operator\", {\n columnName,\n startsWith: partialOperator,\n });\n } else {\n return makeSuggestions(context, \"columnValue\", {\n columnName,\n operator,\n startsWith: word.text,\n });\n }\n }\n\n case \"Identifier\":\n {\n const clauseOperator = getClauseOperator(nodeBefore, state);\n if (clauseOperator === \"as\") {\n return {\n from: context.pos,\n options: [\n {\n label: \"press ENTER to apply filter and save\",\n apply: () => onSubmit.current(),\n boost: 5,\n },\n ],\n };\n }\n }\n break;\n case \"ColumnSetExpression\":\n case \"Values\": {\n const columnName = getNodeByName(nodeBefore, state);\n const selection = getSetValues(nodeBefore, state);\n return makeSuggestions(context, \"columnValue\", {\n columnName,\n selection,\n });\n }\n case \"Comma\":\n case \"LBrack\": {\n const columnName = getNodeByName(nodeBefore, state) as string;\n return makeSuggestions(context, \"columnValue\", { columnName });\n }\n\n case \"ColumnValueExpression\":\n {\n const lastToken = nodeBefore.lastChild?.prevSibling;\n if (lastToken?.name === \"Column\") {\n return makeSuggestions(context, \"operator\", {\n columnName: getNodeByName(nodeBefore, state),\n });\n } else if (lastToken?.name === \"Operator\") {\n return makeSuggestions(context, \"columnValue\", {\n columnName: getNodeByName(lastToken, state),\n operator: getValue(lastToken, state),\n });\n }\n }\n break;\n\n case \"In\": {\n return {\n from: context.pos,\n options: [{ label: \"[\", apply: \" [\", type: \"text\" }],\n };\n }\n\n case \"Eq\": {\n return makeSuggestions(context, \"columnValue\", {\n columnName: getNodeByName(nodeBefore, state),\n });\n }\n\n case \"AndExpression\":\n case \"OrExpression\": {\n return makeSuggestions(context, \"column\");\n }\n\n default:\n }\n },\n [existingFilter, makeSuggestions, onSubmit, suggestionProvider]\n ) as CompletionSource;\n};\n"],"names":["getValue","useCallback","syntaxTree","getNodeByName"],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vuuCodemirror = require('@vuu-ui/vuu-codemirror');
|
|
4
|
+
var vuuDataReact = require('@vuu-ui/vuu-data-react');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var filterInfo = require('./filterInfo.js');
|
|
7
|
+
|
|
8
|
+
const NO_NAMED_FILTERS = [];
|
|
9
|
+
const NONE = {};
|
|
10
|
+
const saveAsTab = (onSubmit) => [
|
|
11
|
+
{
|
|
12
|
+
label: "Press ENTER to create TAB",
|
|
13
|
+
apply: () => onSubmit("tab"),
|
|
14
|
+
boost: 6
|
|
15
|
+
}
|
|
16
|
+
];
|
|
17
|
+
const makeSaveOrExtendSuggestions = (onSubmit, existingFilter, withJoinSuggestions = true) => {
|
|
18
|
+
const result = existingFilter ? [
|
|
19
|
+
{
|
|
20
|
+
label: "REPLACE existing filter",
|
|
21
|
+
apply: () => onSubmit("replace"),
|
|
22
|
+
boost: 8
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: "AND existing filter",
|
|
26
|
+
apply: () => onSubmit("and"),
|
|
27
|
+
boost: 7
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: "OR existing filter",
|
|
31
|
+
apply: () => onSubmit("or"),
|
|
32
|
+
boost: 7
|
|
33
|
+
}
|
|
34
|
+
] : [
|
|
35
|
+
{
|
|
36
|
+
label: "Press ENTER to submit",
|
|
37
|
+
apply: () => onSubmit(),
|
|
38
|
+
boost: 6
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
return withJoinSuggestions ? result.concat(vuuCodemirror.booleanJoinSuggestions).concat(vuuCodemirror.asNameSuggestion) : result;
|
|
42
|
+
};
|
|
43
|
+
const promptToSaveOrExtend = (onSubmit, existingFilter) => makeSaveOrExtendSuggestions(onSubmit, existingFilter, true);
|
|
44
|
+
const promptToSave = (onSubmit) => makeSaveOrExtendSuggestions(onSubmit, void 0);
|
|
45
|
+
const getSaveSuggestions = ({
|
|
46
|
+
existingFilter,
|
|
47
|
+
filterName,
|
|
48
|
+
onSubmit,
|
|
49
|
+
saveOptions
|
|
50
|
+
}) => {
|
|
51
|
+
const includeTabSuggestion = filterName && saveOptions.allowSaveAsTab;
|
|
52
|
+
const result = existingFilter ? promptToSaveOrExtend(onSubmit, existingFilter) : promptToSave(onSubmit);
|
|
53
|
+
if (includeTabSuggestion) {
|
|
54
|
+
return result.concat(saveAsTab(onSubmit));
|
|
55
|
+
} else {
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const suggestColumns = (columns) => columns.map((column) => ({
|
|
60
|
+
boost: 5,
|
|
61
|
+
label: column.name
|
|
62
|
+
}));
|
|
63
|
+
const suggestNamedFilters = (namedFilters) => namedFilters ? Array.from(namedFilters.entries()).map(([filterName, filterQuery]) => ({
|
|
64
|
+
info: () => filterInfo.filterInfo(filterName, filterQuery),
|
|
65
|
+
label: filterName,
|
|
66
|
+
type: "filter"
|
|
67
|
+
})) : NO_NAMED_FILTERS;
|
|
68
|
+
const doneCommand = {
|
|
69
|
+
label: "Done",
|
|
70
|
+
apply: "] ",
|
|
71
|
+
type: "keyword",
|
|
72
|
+
boost: 10
|
|
73
|
+
};
|
|
74
|
+
const withApplySpace = (suggestions, startsWith = "") => suggestions.filter((sugg) => startsWith === "" || sugg.label.startsWith(startsWith)).map((suggestion) => ({
|
|
75
|
+
...suggestion,
|
|
76
|
+
apply: suggestion.label + " "
|
|
77
|
+
}));
|
|
78
|
+
const defaultSaveOptions = {
|
|
79
|
+
allowReplace: true
|
|
80
|
+
};
|
|
81
|
+
const useFilterSuggestionProvider = ({
|
|
82
|
+
columns,
|
|
83
|
+
namedFilters,
|
|
84
|
+
saveOptions = defaultSaveOptions,
|
|
85
|
+
table,
|
|
86
|
+
typeaheadHook: useTypeahead = vuuDataReact.useTypeaheadSuggestions
|
|
87
|
+
}) => {
|
|
88
|
+
const latestSuggestionsRef = react.useRef();
|
|
89
|
+
const getTypeaheadSuggestions = useTypeahead();
|
|
90
|
+
const getSuggestions = react.useCallback(
|
|
91
|
+
async (suggestionType, options = NONE) => {
|
|
92
|
+
const {
|
|
93
|
+
columnName,
|
|
94
|
+
existingFilter,
|
|
95
|
+
filterName,
|
|
96
|
+
operator,
|
|
97
|
+
quoted: autoQuoted,
|
|
98
|
+
onSubmit,
|
|
99
|
+
startsWith,
|
|
100
|
+
selection
|
|
101
|
+
} = options;
|
|
102
|
+
switch (suggestionType) {
|
|
103
|
+
case "operator":
|
|
104
|
+
{
|
|
105
|
+
const column = columns.find((col) => col.name === columnName);
|
|
106
|
+
if (column) {
|
|
107
|
+
switch (column.serverDataType) {
|
|
108
|
+
case "string":
|
|
109
|
+
case "char":
|
|
110
|
+
return withApplySpace(vuuCodemirror.stringOperators, startsWith);
|
|
111
|
+
case "int":
|
|
112
|
+
case "long":
|
|
113
|
+
case "double":
|
|
114
|
+
return withApplySpace(vuuCodemirror.numericOperators);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
console.warn(`'${columnName}' does not match any column name`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
case "column": {
|
|
122
|
+
const columnSuggestions = await suggestColumns(columns);
|
|
123
|
+
const filterSuggestions = await suggestNamedFilters(namedFilters);
|
|
124
|
+
return (latestSuggestionsRef.current = withApplySpace(columnSuggestions)).concat(
|
|
125
|
+
withApplySpace(filterSuggestions)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
case "columnValue":
|
|
129
|
+
{
|
|
130
|
+
if (columnName) {
|
|
131
|
+
const column = columns.find((col) => col.name === columnName);
|
|
132
|
+
if (!column) {
|
|
133
|
+
throw Error(
|
|
134
|
+
`useFilterSUggestionProvider no column ${columnName}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const prefix = Array.isArray(selection) ? selection.length === 0 ? "[" : "," : "";
|
|
138
|
+
const params = vuuDataReact.getTypeaheadParams(
|
|
139
|
+
table,
|
|
140
|
+
columnName,
|
|
141
|
+
startsWith
|
|
142
|
+
);
|
|
143
|
+
const suggestions = await getTypeaheadSuggestions(params);
|
|
144
|
+
const isIllustration = operator === "starts";
|
|
145
|
+
latestSuggestionsRef.current = vuuCodemirror.toSuggestions(suggestions, {
|
|
146
|
+
moveCursorToEnd: autoQuoted,
|
|
147
|
+
quoted: column?.serverDataType === "string" && !autoQuoted,
|
|
148
|
+
suffix: autoQuoted ? "" : " ",
|
|
149
|
+
prefix: isIllustration ? startsWith : prefix,
|
|
150
|
+
isIllustration
|
|
151
|
+
});
|
|
152
|
+
if (Array.isArray(selection) && selection?.length > 1) {
|
|
153
|
+
return [doneCommand, ...latestSuggestionsRef.current];
|
|
154
|
+
}
|
|
155
|
+
return latestSuggestionsRef.current;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
case "save": {
|
|
160
|
+
if (typeof onSubmit !== "function") {
|
|
161
|
+
throw Error(
|
|
162
|
+
"useFilterSuggestionProvider, onSubmit must be supplied for 'save' suggestions"
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return await getSaveSuggestions({
|
|
166
|
+
existingFilter,
|
|
167
|
+
filterName,
|
|
168
|
+
onSubmit,
|
|
169
|
+
saveOptions
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
case "name":
|
|
173
|
+
return await vuuCodemirror.getNamePrompt("filter");
|
|
174
|
+
}
|
|
175
|
+
return [];
|
|
176
|
+
},
|
|
177
|
+
[columns, getTypeaheadSuggestions, namedFilters, saveOptions, table]
|
|
178
|
+
);
|
|
179
|
+
const isPartialMatch = react.useCallback(
|
|
180
|
+
async (valueType, columnName, pattern) => {
|
|
181
|
+
const suggestions = (
|
|
182
|
+
// latestSuggestions && latestSuggestions.length > 0
|
|
183
|
+
// ? latestSuggestions
|
|
184
|
+
await getSuggestions(valueType, { columnName })
|
|
185
|
+
);
|
|
186
|
+
if (pattern && suggestions) {
|
|
187
|
+
for (const option of suggestions) {
|
|
188
|
+
if (option.label === pattern) {
|
|
189
|
+
return false;
|
|
190
|
+
} else if (option.label.startsWith(pattern)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
},
|
|
197
|
+
[getSuggestions]
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
getSuggestions,
|
|
201
|
+
isPartialMatch
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
exports.useFilterSuggestionProvider = useFilterSuggestionProvider;
|
|
206
|
+
//# sourceMappingURL=useFilterSuggestionProvider.js.map
|