@jupytergis/base 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/BaseCommandIDs.d.ts +2 -0
- package/lib/commands/BaseCommandIDs.js +3 -0
- package/lib/commands/index.js +66 -0
- package/lib/constants.js +4 -0
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +0 -6
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +2 -2
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +4 -4
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +1 -5
- package/lib/formbuilder/formselectors.js +5 -1
- package/lib/formbuilder/objectform/StoryEditorForm.d.ts +9 -0
- package/lib/formbuilder/objectform/StoryEditorForm.js +16 -0
- package/lib/formbuilder/objectform/components/StorySegmentReset.d.ts +8 -0
- package/lib/formbuilder/objectform/components/StorySegmentReset.js +24 -0
- package/lib/formbuilder/objectform/layer/index.d.ts +1 -0
- package/lib/formbuilder/objectform/layer/index.js +1 -0
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +5 -0
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +32 -0
- package/lib/mainview/mainView.d.ts +18 -0
- package/lib/mainview/mainView.js +293 -14
- package/lib/panelview/components/layers.d.ts +2 -1
- package/lib/panelview/components/layers.js +31 -23
- package/lib/panelview/{components/filter-panel → filter-panel}/Filter.js +1 -1
- package/lib/panelview/leftpanel.js +89 -7
- package/lib/panelview/rightpanel.d.ts +2 -0
- package/lib/panelview/rightpanel.js +41 -4
- package/lib/panelview/story-maps/PreviewModeSwitch.d.ts +7 -0
- package/lib/panelview/story-maps/PreviewModeSwitch.js +13 -0
- package/lib/panelview/story-maps/StoryEditorPanel.d.ts +9 -0
- package/lib/panelview/story-maps/StoryEditorPanel.js +34 -0
- package/lib/panelview/story-maps/StoryNavBar.d.ts +10 -0
- package/lib/panelview/story-maps/StoryNavBar.js +11 -0
- package/lib/panelview/story-maps/StoryViewerPanel.d.ts +13 -0
- package/lib/panelview/story-maps/StoryViewerPanel.js +179 -0
- package/lib/panelview/story-maps/components/StoryContentSection.d.ts +6 -0
- package/lib/panelview/story-maps/components/StoryContentSection.js +10 -0
- package/lib/panelview/story-maps/components/StoryImageSection.d.ts +15 -0
- package/lib/panelview/story-maps/components/StoryImageSection.js +13 -0
- package/lib/panelview/story-maps/components/StorySubtitleSection.d.ts +11 -0
- package/lib/panelview/story-maps/components/StorySubtitleSection.js +9 -0
- package/lib/panelview/story-maps/components/StoryTitleSection.d.ts +12 -0
- package/lib/panelview/story-maps/components/StoryTitleSection.js +8 -0
- package/lib/shared/components/Calendar.d.ts +1 -1
- package/lib/shared/components/Combobox.d.ts +21 -0
- package/lib/shared/components/Combobox.js +32 -0
- package/lib/shared/components/Command.d.ts +18 -0
- package/lib/shared/components/Command.js +60 -0
- package/lib/shared/components/Dialog.d.ts +15 -0
- package/lib/shared/components/Dialog.js +62 -0
- package/lib/shared/components/Input.d.ts +3 -0
- package/lib/shared/components/Input.js +18 -0
- package/lib/shared/components/Pagination.js +3 -2
- package/lib/shared/components/RadioGroup.d.ts +5 -0
- package/lib/shared/components/RadioGroup.js +26 -0
- package/lib/shared/components/Select.d.ts +19 -0
- package/lib/shared/components/Select.js +28 -0
- package/lib/shared/components/SingleDatePicker.d.ts +11 -0
- package/lib/shared/components/SingleDatePicker.js +16 -0
- package/lib/shared/components/Switch.d.ts +4 -0
- package/lib/shared/components/Switch.js +20 -0
- package/lib/stacBrowser/components/StacPanel.d.ts +9 -1
- package/lib/stacBrowser/components/StacPanel.js +53 -9
- package/lib/stacBrowser/components/filter-extension/QueryableComboBox.d.ts +9 -0
- package/lib/stacBrowser/components/filter-extension/QueryableComboBox.js +179 -0
- package/lib/stacBrowser/components/filter-extension/QueryableRow.d.ts +16 -0
- package/lib/stacBrowser/components/filter-extension/QueryableRow.js +16 -0
- package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.d.ts +7 -0
- package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.js +49 -0
- package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.d.ts +11 -0
- package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.js +19 -0
- package/lib/stacBrowser/components/{StacFilterSection.d.ts → geodes/StacFilterSection.d.ts} +1 -1
- package/lib/stacBrowser/components/{StacFilterSection.js → geodes/StacFilterSection.js} +3 -3
- package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.d.ts +7 -0
- package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.js +69 -0
- package/lib/stacBrowser/components/shared/StacPanelResults.d.ts +3 -0
- package/lib/stacBrowser/components/shared/StacPanelResults.js +68 -0
- package/lib/stacBrowser/components/shared/StacSpatialExtent.d.ts +8 -0
- package/lib/stacBrowser/components/shared/StacSpatialExtent.js +10 -0
- package/lib/stacBrowser/components/shared/StacTemporalExtent.d.ts +9 -0
- package/lib/stacBrowser/components/shared/StacTemporalExtent.js +9 -0
- package/lib/stacBrowser/context/StacResultsContext.d.ts +33 -0
- package/lib/stacBrowser/context/StacResultsContext.js +269 -0
- package/lib/stacBrowser/hooks/useGeodesSearch.d.ts +24 -0
- package/lib/stacBrowser/hooks/useGeodesSearch.js +178 -0
- package/lib/stacBrowser/hooks/useStacFilterExtension.d.ts +30 -0
- package/lib/stacBrowser/hooks/useStacFilterExtension.js +262 -0
- package/lib/stacBrowser/hooks/useStacSearch.d.ts +5 -16
- package/lib/stacBrowser/hooks/useStacSearch.js +30 -184
- package/lib/stacBrowser/types/types.d.ts +86 -3
- package/lib/toolbar/widget.d.ts +15 -0
- package/lib/toolbar/widget.js +70 -0
- package/lib/tools.d.ts +0 -7
- package/lib/tools.js +56 -15
- package/package.json +8 -3
- package/style/base.css +42 -3
- package/style/leftPanel.css +18 -0
- package/style/shared/button.css +6 -5
- package/style/shared/calendar.css +7 -1
- package/style/shared/combobox.css +75 -0
- package/style/shared/command.css +178 -0
- package/style/shared/dialog.css +177 -0
- package/style/shared/input.css +59 -0
- package/style/shared/pagination.css +1 -1
- package/style/shared/popover.css +1 -0
- package/style/shared/radioGroup.css +55 -0
- package/style/shared/switch.css +63 -0
- package/style/shared/tabs.css +4 -3
- package/style/shared/toggle.css +1 -1
- package/style/stacBrowser.css +169 -16
- package/style/statusBar.css +1 -0
- package/style/storyPanel.css +185 -0
- package/style/tabPanel.css +1 -88
- package/lib/stacBrowser/components/StacPanelFilters.d.ts +0 -14
- package/lib/stacBrowser/components/StacPanelFilters.js +0 -81
- package/lib/stacBrowser/components/StacPanelResults.d.ts +0 -13
- package/lib/stacBrowser/components/StacPanelResults.js +0 -48
- /package/lib/panelview/{components/filter-panel → filter-panel}/Filter.d.ts +0 -0
- /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.d.ts +0 -0
- /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.js +0 -0
- /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.d.ts +0 -0
- /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.js +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { Combobox } from "../../../shared/components/Combobox";
|
|
3
|
+
import { Input } from "../../../shared/components/Input";
|
|
4
|
+
import { Select } from "../../../shared/components/Select";
|
|
5
|
+
import QueryableRow from "./QueryableRow";
|
|
6
|
+
import SingleDatePicker from '../../../shared/components/SingleDatePicker';
|
|
7
|
+
export function QueryableComboBox({ queryables, selectedQueryables, updateSelectedQueryables, }) {
|
|
8
|
+
// Derive selected items from selectedQueryables
|
|
9
|
+
const selectedItems = useMemo(() => {
|
|
10
|
+
return queryables.filter(([key]) => key in selectedQueryables);
|
|
11
|
+
}, [queryables, selectedQueryables]);
|
|
12
|
+
const handleSelect = (key, val) => {
|
|
13
|
+
var _a;
|
|
14
|
+
const isCurrentlySelected = key in selectedQueryables;
|
|
15
|
+
if (isCurrentlySelected) {
|
|
16
|
+
// Remove if already selected - pass null to explicitly remove
|
|
17
|
+
updateSelectedQueryables(key, null);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
// Add if not selected - initialize with default filter
|
|
21
|
+
const operators = getOperatorsForType(val.type, val.format);
|
|
22
|
+
let initialInputValue = undefined;
|
|
23
|
+
// For enum types, set the first option since the UI looks like there's a selection
|
|
24
|
+
if (val.enum && val.enum.length > 0) {
|
|
25
|
+
initialInputValue =
|
|
26
|
+
typeof val.enum[0] === 'number' ? val.enum[0] : val.enum[0];
|
|
27
|
+
}
|
|
28
|
+
else if (val.type === 'string' && val.format === 'date-time') {
|
|
29
|
+
// For datetime types, set to current UTC time
|
|
30
|
+
initialInputValue = new Date().toISOString();
|
|
31
|
+
}
|
|
32
|
+
updateSelectedQueryables(key, {
|
|
33
|
+
operator: ((_a = operators[0]) === null || _a === void 0 ? void 0 : _a.value) || '=',
|
|
34
|
+
inputValue: initialInputValue,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const getOperatorsForType = (type, format) => {
|
|
39
|
+
if (format === 'date-time') {
|
|
40
|
+
return [
|
|
41
|
+
{ value: '<', label: '<' },
|
|
42
|
+
{ value: '<=', label: '≤' },
|
|
43
|
+
{ value: '>', label: '>' },
|
|
44
|
+
{ value: '>=', label: '≥' },
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
switch (type) {
|
|
48
|
+
case 'string':
|
|
49
|
+
return [
|
|
50
|
+
{ value: '=', label: '=' },
|
|
51
|
+
{ value: '!=', label: '≠' },
|
|
52
|
+
];
|
|
53
|
+
case 'number':
|
|
54
|
+
case 'integer':
|
|
55
|
+
return [
|
|
56
|
+
{ value: '=', label: '=' },
|
|
57
|
+
{ value: '!=', label: '≠' },
|
|
58
|
+
{ value: '<', label: '<' },
|
|
59
|
+
{ value: '<=', label: '≤' },
|
|
60
|
+
{ value: '>', label: '>' },
|
|
61
|
+
{ value: '>=', label: '≥' },
|
|
62
|
+
];
|
|
63
|
+
default:
|
|
64
|
+
return [
|
|
65
|
+
{ value: '=', label: '=' },
|
|
66
|
+
{ value: '!=', label: '≠' },
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const getInputBasedOnType = (val, currentValue, onChange) => {
|
|
71
|
+
switch (val.type) {
|
|
72
|
+
case 'string':
|
|
73
|
+
if (val.enum) {
|
|
74
|
+
const selectedOption = val.enum.find(opt => String(opt) === String(currentValue));
|
|
75
|
+
const buttonText = selectedOption
|
|
76
|
+
? String(selectedOption)
|
|
77
|
+
: 'Select option...';
|
|
78
|
+
return (React.createElement(Select, { items: val.enum.map(option => ({
|
|
79
|
+
value: String(option),
|
|
80
|
+
label: String(option),
|
|
81
|
+
onSelect: () => onChange(String(option)),
|
|
82
|
+
})), buttonText: buttonText, emptyText: "No option found.", buttonClassName: "jgis-queryable-combo-input" }));
|
|
83
|
+
}
|
|
84
|
+
if (val.format === 'date-time') {
|
|
85
|
+
// Convert UTC ISO string to Date object for SingleDatePicker
|
|
86
|
+
const parseDate = (isoString) => {
|
|
87
|
+
if (!isoString) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return new Date(isoString);
|
|
92
|
+
}
|
|
93
|
+
catch (_a) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
// Convert Date object back to ISO string for storage
|
|
98
|
+
const handleDateChange = (date) => {
|
|
99
|
+
if (date) {
|
|
100
|
+
onChange(date.toISOString());
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
onChange('');
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return (React.createElement(SingleDatePicker, { date: parseDate(currentValue), onDateChange: handleDateChange, dateFormat: "P", showIcon: true, placeholder: "Select date", className: "jgis-queryable-combo-input jgis-queryable-combo-input-date-picker" }));
|
|
107
|
+
}
|
|
108
|
+
return (React.createElement(Input, { type: "text", className: "jgis-queryable-combo-input",
|
|
109
|
+
// style={{borderRadius: 0}}
|
|
110
|
+
value: currentValue || '', onChange: e => onChange(e.target.value) }));
|
|
111
|
+
case 'number':
|
|
112
|
+
case 'integer':
|
|
113
|
+
if (val.enum) {
|
|
114
|
+
const selectedOption = val.enum.find(opt => Number(opt) === Number(currentValue));
|
|
115
|
+
const buttonText = selectedOption
|
|
116
|
+
? String(selectedOption)
|
|
117
|
+
: 'Select option...';
|
|
118
|
+
return (React.createElement(Select, { items: val.enum.map(option => ({
|
|
119
|
+
value: String(option),
|
|
120
|
+
label: String(option),
|
|
121
|
+
onSelect: () => onChange(Number(option)),
|
|
122
|
+
})), buttonText: buttonText, emptyText: "No option found.", buttonClassName: "jgis-queryable-combo-input" }));
|
|
123
|
+
}
|
|
124
|
+
return (React.createElement(Input, { type: "number", className: "jgis-queryable-combo-input", min: val.minimum !== undefined ? val.minimum : undefined, max: val.maximum !== undefined ? val.maximum : undefined, value: currentValue || '', onChange: e => onChange(Number(e.target.value)) }));
|
|
125
|
+
default:
|
|
126
|
+
return (React.createElement(Input, { type: "text", className: "jgis-queryable-combo-input", value: currentValue || '', onChange: e => onChange(e.target.value) }));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const getButtonText = () => {
|
|
130
|
+
if (selectedItems.length === 0) {
|
|
131
|
+
return 'Select queryable...';
|
|
132
|
+
}
|
|
133
|
+
if (selectedItems.length === 1) {
|
|
134
|
+
return selectedItems[0][1].title || selectedItems[0][0] || 'Queryable';
|
|
135
|
+
}
|
|
136
|
+
return `${selectedItems.length} selected`;
|
|
137
|
+
};
|
|
138
|
+
const isSelected = (key) => {
|
|
139
|
+
return key in selectedQueryables;
|
|
140
|
+
};
|
|
141
|
+
const items = queryables.map(([key, val]) => ({
|
|
142
|
+
value: key,
|
|
143
|
+
label: val.title || key,
|
|
144
|
+
selected: isSelected(key),
|
|
145
|
+
showCheckIcon: true,
|
|
146
|
+
onSelect: () => handleSelect(key, val),
|
|
147
|
+
}));
|
|
148
|
+
return (React.createElement("div", { className: "jgis-queryable-combo-container" },
|
|
149
|
+
React.createElement(Combobox, { items: items, buttonText: getButtonText(), searchPlaceholder: "Search queryable...", emptyText: "No queryable found.", buttonClassName: "jgis-queryable-combo-button jgis-combobox-button--full-width" }),
|
|
150
|
+
React.createElement("div", { className: "jgis-queryable-rows-container" }, selectedItems.map(([key, val]) => {
|
|
151
|
+
var _a, _b;
|
|
152
|
+
const operators = getOperatorsForType(val.type, val.format);
|
|
153
|
+
const currentFilter = (_a = selectedQueryables[key]) !== null && _a !== void 0 ? _a : {
|
|
154
|
+
operator: ((_b = operators[0]) === null || _b === void 0 ? void 0 : _b.value) || '=',
|
|
155
|
+
inputValue: undefined,
|
|
156
|
+
};
|
|
157
|
+
const handleInputChange = (value) => {
|
|
158
|
+
// For datetime values, convert local time to UTC ISO string
|
|
159
|
+
let valueToStore = value;
|
|
160
|
+
if (val.type === 'string' &&
|
|
161
|
+
val.format === 'date-time' &&
|
|
162
|
+
typeof value === 'string') {
|
|
163
|
+
try {
|
|
164
|
+
// Parse local time and convert to UTC ISO string
|
|
165
|
+
const localDate = new Date(value);
|
|
166
|
+
valueToStore = localDate.toISOString();
|
|
167
|
+
}
|
|
168
|
+
catch (_a) {
|
|
169
|
+
valueToStore = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
updateSelectedQueryables(key, Object.assign(Object.assign({}, currentFilter), { inputValue: valueToStore }));
|
|
173
|
+
};
|
|
174
|
+
const handleOperatorChange = (operator) => {
|
|
175
|
+
updateSelectedQueryables(key, Object.assign(Object.assign({}, currentFilter), { operator }));
|
|
176
|
+
};
|
|
177
|
+
return (React.createElement(QueryableRow, { key: key, qKey: key, qVal: val, operators: operators, currentFilter: currentFilter, inputComponent: getInputBasedOnType(val, currentFilter.inputValue, handleInputChange), onOperatorChange: handleOperatorChange }));
|
|
178
|
+
}))));
|
|
179
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IQueryableFilter, IStacQueryableSchema, Operator } from "../../types/types";
|
|
3
|
+
interface IOperatorOption {
|
|
4
|
+
value: Operator;
|
|
5
|
+
label: string | React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
interface IQueryableRowProps {
|
|
8
|
+
qKey: string;
|
|
9
|
+
qVal: IStacQueryableSchema;
|
|
10
|
+
operators: IOperatorOption[];
|
|
11
|
+
currentFilter: IQueryableFilter;
|
|
12
|
+
inputComponent: React.ReactNode;
|
|
13
|
+
onOperatorChange: (operator: Operator) => void;
|
|
14
|
+
}
|
|
15
|
+
declare function QueryableRow({ qKey, qVal, operators, currentFilter, inputComponent, onOperatorChange, }: IQueryableRowProps): React.JSX.Element;
|
|
16
|
+
export default QueryableRow;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Select } from "../../../shared/components/Select";
|
|
3
|
+
function QueryableRow({ qKey, qVal, operators, currentFilter, inputComponent, onOperatorChange, }) {
|
|
4
|
+
const currentOperator = operators.find(op => op.value === currentFilter.operator);
|
|
5
|
+
const buttonText = (currentOperator === null || currentOperator === void 0 ? void 0 : currentOperator.label) || 'Select operator...';
|
|
6
|
+
const items = operators.map(operator => ({
|
|
7
|
+
value: String(operator.value),
|
|
8
|
+
label: String(operator.label),
|
|
9
|
+
onSelect: () => onOperatorChange(operator.value),
|
|
10
|
+
}));
|
|
11
|
+
return (React.createElement("div", { className: "jgis-queryable-row" },
|
|
12
|
+
React.createElement("span", null, qVal.title || qKey),
|
|
13
|
+
React.createElement(Select, { items: items, buttonText: String(buttonText), emptyText: "No operator found.", buttonClassName: "jgis-queryable-combo-operator" }),
|
|
14
|
+
inputComponent));
|
|
15
|
+
}
|
|
16
|
+
export default QueryableRow;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
interface IStacFilterExtensionPanelProps {
|
|
4
|
+
model?: IJupyterGISModel;
|
|
5
|
+
}
|
|
6
|
+
declare function StacFilterExtensionPanel({ model }: IStacFilterExtensionPanelProps): React.JSX.Element | undefined;
|
|
7
|
+
export default StacFilterExtensionPanel;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Input } from "../../../shared/components/Input";
|
|
3
|
+
import { Select } from "../../../shared/components/Select";
|
|
4
|
+
import StacQueryableFilters from "./StacQueryableFilters";
|
|
5
|
+
import StacSpatialExtent from "../shared/StacSpatialExtent";
|
|
6
|
+
import StacTemporalExtent from "../shared/StacTemporalExtent";
|
|
7
|
+
import { useStacResultsContext } from "../../context/StacResultsContext";
|
|
8
|
+
import { useStacFilterExtension } from "../../hooks/useStacFilterExtension";
|
|
9
|
+
function StacFilterExtensionPanel({ model }) {
|
|
10
|
+
var _a;
|
|
11
|
+
const { selectedUrl } = useStacResultsContext();
|
|
12
|
+
const [limit, setLimit] = useState(12);
|
|
13
|
+
const { queryableFields, collections, selectedCollection, setSelectedCollection, startTime, endTime, setStartTime, setEndTime, useWorldBBox, setUseWorldBBox, selectedQueryables, updateSelectedQueryables, filterOperator, setFilterOperator, } = useStacFilterExtension({
|
|
14
|
+
model,
|
|
15
|
+
baseUrl: selectedUrl,
|
|
16
|
+
limit,
|
|
17
|
+
});
|
|
18
|
+
if (!model) {
|
|
19
|
+
console.warn('JupyterGIS model not found');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
return (React.createElement(React.Fragment, null,
|
|
23
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
24
|
+
React.createElement(StacTemporalExtent, { startTime: startTime, endTime: endTime, setStartTime: setStartTime, setEndTime: setEndTime })),
|
|
25
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
26
|
+
React.createElement(StacSpatialExtent, { checked: useWorldBBox, onCheckedChange: setUseWorldBBox, label: "Use entire world" })),
|
|
27
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
28
|
+
React.createElement("label", { className: "jgis-stac-filter-extension-label" }, "Collection"),
|
|
29
|
+
React.createElement(Select, { items: collections.map((option) => ({
|
|
30
|
+
value: option.id,
|
|
31
|
+
label: option.title || option.id,
|
|
32
|
+
onSelect: () => setSelectedCollection(option.id),
|
|
33
|
+
})), buttonText: selectedCollection
|
|
34
|
+
? ((_a = collections.find(c => c.id === selectedCollection)) === null || _a === void 0 ? void 0 : _a.title) ||
|
|
35
|
+
'Select a collection...'
|
|
36
|
+
: 'Select a collection...', emptyText: "No collection found.", buttonClassName: "jgis-stac-filter-extension-select", showSearch: true, searchPlaceholder: "Search collections..." })),
|
|
37
|
+
queryableFields && (React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
38
|
+
React.createElement("label", { className: "jgis-stac-filter-extension-label" }, "Additional Filters"),
|
|
39
|
+
React.createElement(StacQueryableFilters, { queryableFields: queryableFields, selectedQueryables: selectedQueryables, updateSelectedQueryables: updateSelectedQueryables, filterOperator: filterOperator, setFilterOperator: setFilterOperator }))),
|
|
40
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
41
|
+
React.createElement("label", { className: "jgis-stac-filter-extension-label" }, "Items per page"),
|
|
42
|
+
React.createElement(Input, { type: "number", min: "1", max: "1000", value: limit, onChange: e => {
|
|
43
|
+
const value = parseInt(e.target.value, 10);
|
|
44
|
+
if (!isNaN(value) && value > 0) {
|
|
45
|
+
setLimit(value);
|
|
46
|
+
}
|
|
47
|
+
}, className: "jgis-stac-filter-extension-input" }))));
|
|
48
|
+
}
|
|
49
|
+
export default StacFilterExtensionPanel;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FilterOperator, IQueryableFilter, IStacQueryables, UpdateSelectedQueryables } from "../../types/types";
|
|
3
|
+
interface IStacQueryableFilterListProps {
|
|
4
|
+
queryableFields: IStacQueryables;
|
|
5
|
+
selectedQueryables: Record<string, IQueryableFilter>;
|
|
6
|
+
updateSelectedQueryables: UpdateSelectedQueryables;
|
|
7
|
+
filterOperator: FilterOperator;
|
|
8
|
+
setFilterOperator: (operator: FilterOperator) => void;
|
|
9
|
+
}
|
|
10
|
+
declare const StacQueryableFilters: React.FC<IStacQueryableFilterListProps>;
|
|
11
|
+
export default StacQueryableFilters;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { RadioGroup, RadioGroupItem } from "../../../shared/components/RadioGroup";
|
|
3
|
+
import { QueryableComboBox } from "./QueryableComboBox";
|
|
4
|
+
const StacQueryableFilters = ({ queryableFields, selectedQueryables, updateSelectedQueryables, filterOperator, setFilterOperator, }) => {
|
|
5
|
+
return (React.createElement("div", { className: "jgis-stac-queryable-filters" },
|
|
6
|
+
React.createElement(RadioGroup, { className: "jgis-stac-queryable-filters-radio-group", value: filterOperator, onValueChange: (value) => {
|
|
7
|
+
if (value === 'and' || value === 'or') {
|
|
8
|
+
setFilterOperator(value);
|
|
9
|
+
}
|
|
10
|
+
} },
|
|
11
|
+
React.createElement("div", { className: "jgis-stac-queryable-filters-radio-item" },
|
|
12
|
+
React.createElement(RadioGroupItem, { value: "and", id: "filter-operator-and" }),
|
|
13
|
+
React.createElement("label", { htmlFor: "filter-operator-and" }, "Match all filters (and)")),
|
|
14
|
+
React.createElement("div", { className: "jgis-stac-queryable-filters-radio-item" },
|
|
15
|
+
React.createElement(RadioGroupItem, { value: "or", id: "filter-operator-or" }),
|
|
16
|
+
React.createElement("label", { htmlFor: "filter-operator-or" }, "Match any filters (or)"))),
|
|
17
|
+
React.createElement(QueryableComboBox, { queryables: queryableFields, selectedQueryables: selectedQueryables, updateSelectedQueryables: updateSelectedQueryables })));
|
|
18
|
+
};
|
|
19
|
+
export default StacQueryableFilters;
|
|
@@ -2,9 +2,9 @@ import { faXmark } from '@fortawesome/free-solid-svg-icons';
|
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
3
3
|
import { ChevronRight } from 'lucide-react';
|
|
4
4
|
import React, { useMemo } from 'react';
|
|
5
|
-
import Badge from "
|
|
6
|
-
import { Button } from "
|
|
7
|
-
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "
|
|
5
|
+
import Badge from "../../../shared/components/Badge";
|
|
6
|
+
import { Button } from "../../../shared/components/Button";
|
|
7
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "../../../shared/components/DropdownMenu";
|
|
8
8
|
const StacFilterSection = ({ section, data, selectedCollections, selectedData, handleCheckedChange, }) => {
|
|
9
9
|
const items = useMemo(() => {
|
|
10
10
|
if (section === 'Collection') {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
interface IStacGeodesFilterPanelProps {
|
|
4
|
+
model?: IJupyterGISModel;
|
|
5
|
+
}
|
|
6
|
+
declare const StacGeodesFilterPanel: ({ model }: IStacGeodesFilterPanelProps) => React.JSX.Element;
|
|
7
|
+
export default StacGeodesFilterPanel;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import StacFilterSection from "./StacFilterSection";
|
|
3
|
+
import StacSpatialExtent from "../shared/StacSpatialExtent";
|
|
4
|
+
import StacTemporalExtent from "../shared/StacTemporalExtent";
|
|
5
|
+
import { datasets as datasetsList, platforms as platformsList, products as productsList, } from "../../constants";
|
|
6
|
+
import useGeodesSearch from "../../hooks/useGeodesSearch";
|
|
7
|
+
const StacGeodesFilterPanel = ({ model }) => {
|
|
8
|
+
const { filterState, filterSetters, startTime, setStartTime, endTime, setEndTime, useWorldBBox, setUseWorldBBox, } = useGeodesSearch({
|
|
9
|
+
model,
|
|
10
|
+
});
|
|
11
|
+
const handleDatasetSelection = (dataset, collection) => {
|
|
12
|
+
const collections = new Set(filterState.collections);
|
|
13
|
+
const datasets = new Set(filterState.datasets);
|
|
14
|
+
if (datasets.has(dataset)) {
|
|
15
|
+
datasets.delete(dataset);
|
|
16
|
+
// Remove the collection if no datasets remain for it
|
|
17
|
+
const datasetsForCollection = Array.from(datasets).filter(d => {
|
|
18
|
+
return datasetsList.some(entry => entry.collection === collection && entry.datasets.includes(d));
|
|
19
|
+
});
|
|
20
|
+
if (datasetsForCollection.length === 0) {
|
|
21
|
+
collections.delete(collection);
|
|
22
|
+
const platforms = new Set(filterState.platforms);
|
|
23
|
+
const products = new Set(filterState.products);
|
|
24
|
+
// Remove platforms belonging to this collection
|
|
25
|
+
if (platformsList[collection]) {
|
|
26
|
+
platformsList[collection].forEach(platform => {
|
|
27
|
+
platforms.delete(platform);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// Remove products belonging to this collection
|
|
31
|
+
productsList
|
|
32
|
+
.filter(product => product.collections.includes(collection))
|
|
33
|
+
.forEach(product => {
|
|
34
|
+
products.delete(product.productCode);
|
|
35
|
+
});
|
|
36
|
+
filterSetters.platforms(platforms);
|
|
37
|
+
filterSetters.products(products);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
datasets.add(dataset);
|
|
42
|
+
collections.add(collection);
|
|
43
|
+
}
|
|
44
|
+
filterSetters.collections(collections);
|
|
45
|
+
filterSetters.datasets(datasets);
|
|
46
|
+
};
|
|
47
|
+
const handleToggle = (key, value) => {
|
|
48
|
+
const updated = new Set(filterState[key]);
|
|
49
|
+
if (updated.has(value)) {
|
|
50
|
+
updated.delete(value);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
updated.add(value);
|
|
54
|
+
}
|
|
55
|
+
filterSetters[key](updated);
|
|
56
|
+
};
|
|
57
|
+
return (React.createElement(React.Fragment, null,
|
|
58
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
59
|
+
React.createElement(StacSpatialExtent, { checked: useWorldBBox, onCheckedChange: setUseWorldBBox, label: "Use whole world as bounding box" })),
|
|
60
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
61
|
+
React.createElement(StacTemporalExtent, { startTime: startTime, setStartTime: setStartTime, endTime: endTime, setEndTime: setEndTime })),
|
|
62
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
63
|
+
React.createElement(StacFilterSection, { section: "Collection", data: datasetsList, selectedCollections: Array.from(filterState.collections), selectedData: Array.from(filterState.datasets), handleCheckedChange: handleDatasetSelection })),
|
|
64
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
65
|
+
React.createElement(StacFilterSection, { section: "Platform", data: platformsList, selectedCollections: Array.from(filterState.collections), selectedData: Array.from(filterState.platforms), handleCheckedChange: platform => handleToggle('platforms', platform) })),
|
|
66
|
+
React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
67
|
+
React.createElement(StacFilterSection, { section: "Data / Product", data: productsList, selectedCollections: Array.from(filterState.collections), selectedData: Array.from(filterState.products), handleCheckedChange: product => handleToggle('products', product) }))));
|
|
68
|
+
};
|
|
69
|
+
export default StacGeodesFilterPanel;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button } from "../../../shared/components/Button";
|
|
3
|
+
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "../../../shared/components/Pagination";
|
|
4
|
+
import { LoadingIcon } from "../../../shared/components/loading";
|
|
5
|
+
import { useStacResultsContext } from "../../context/StacResultsContext";
|
|
6
|
+
function getPageItems(currentPage, totalPages) {
|
|
7
|
+
if (totalPages <= 5) {
|
|
8
|
+
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
9
|
+
}
|
|
10
|
+
if (currentPage <= 3) {
|
|
11
|
+
return [1, 2, 3, 'ellipsis', totalPages];
|
|
12
|
+
}
|
|
13
|
+
if (currentPage >= totalPages - 2) {
|
|
14
|
+
return [
|
|
15
|
+
totalPages - 4,
|
|
16
|
+
totalPages - 3,
|
|
17
|
+
totalPages - 2,
|
|
18
|
+
totalPages - 1,
|
|
19
|
+
totalPages,
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
currentPage - 2,
|
|
24
|
+
currentPage - 1,
|
|
25
|
+
currentPage,
|
|
26
|
+
'ellipsis',
|
|
27
|
+
totalPages,
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
const StacPanelResults = () => {
|
|
31
|
+
const { results, handlePaginationClick, handleResultClick, formatResult, isLoading, paginationLinks, currentPage, setCurrentPage, totalPages, executeQueryWithPage, } = useStacResultsContext();
|
|
32
|
+
const isNext = paginationLinks.some(link => link.rel === 'next');
|
|
33
|
+
const isPrev = paginationLinks.some(link => ['prev', 'previous'].includes(link.rel));
|
|
34
|
+
return (React.createElement("div", { className: "jgis-stac-browser-filters-panel" },
|
|
35
|
+
React.createElement(Pagination, null,
|
|
36
|
+
React.createElement(PaginationContent, { className: "jgis-stac-panel-results-pagination" },
|
|
37
|
+
React.createElement(PaginationItem, null,
|
|
38
|
+
React.createElement(PaginationPrevious, { onClick: () => {
|
|
39
|
+
setCurrentPage(Math.max(currentPage - 1, 1));
|
|
40
|
+
handlePaginationClick('previous');
|
|
41
|
+
}, disabled: !isPrev })),
|
|
42
|
+
totalPages === 1 ? (
|
|
43
|
+
// One page, display current page number and keep active
|
|
44
|
+
React.createElement(PaginationItem, null,
|
|
45
|
+
React.createElement(PaginationLink, { isActive: true }, currentPage))) : results.length !== 0 ? (
|
|
46
|
+
// Multiple pages, display fancy pagination numbers
|
|
47
|
+
React.createElement(React.Fragment, null, getPageItems(currentPage, totalPages).map(pageNumber => {
|
|
48
|
+
if (pageNumber === 'ellipsis') {
|
|
49
|
+
return (React.createElement(PaginationItem, { key: "ellipsis" },
|
|
50
|
+
React.createElement(PaginationEllipsis, null)));
|
|
51
|
+
}
|
|
52
|
+
return (React.createElement(PaginationItem, { key: pageNumber },
|
|
53
|
+
React.createElement(PaginationLink, { isActive: pageNumber === currentPage, onClick: async () => {
|
|
54
|
+
setCurrentPage(pageNumber);
|
|
55
|
+
await executeQueryWithPage(pageNumber);
|
|
56
|
+
}, disabled: totalPages === 1 }, pageNumber)));
|
|
57
|
+
}))) : (
|
|
58
|
+
// No results
|
|
59
|
+
React.createElement(PaginationItem, null,
|
|
60
|
+
React.createElement(PaginationLink, { isActive: true, disabled: true }, "0"))),
|
|
61
|
+
React.createElement(PaginationItem, null,
|
|
62
|
+
React.createElement(PaginationNext, { onClick: () => {
|
|
63
|
+
setCurrentPage(currentPage + 1);
|
|
64
|
+
handlePaginationClick('next');
|
|
65
|
+
}, disabled: !isNext })))),
|
|
66
|
+
React.createElement("div", { className: "jgis-stac-browser-results-list" }, isLoading ? (React.createElement(LoadingIcon, { size: "3x" })) : (results.map(result => (React.createElement(Button, { key: result.id, className: "jgis-stac-browser-results-item", onClick: () => handleResultClick(result.id) }, formatResult(result))))))));
|
|
67
|
+
};
|
|
68
|
+
export default StacPanelResults;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Checkbox from "../../../shared/components/Checkbox";
|
|
3
|
+
const StacSpatialExtent = ({ checked, onCheckedChange, label, }) => {
|
|
4
|
+
return (React.createElement(React.Fragment, null,
|
|
5
|
+
React.createElement("label", { className: "jgis-stac-filter-extension-label" }, "Spatial Extent"),
|
|
6
|
+
React.createElement("span", { style: { display: 'flex', alignItems: 'center', gap: '0.5rem' } },
|
|
7
|
+
React.createElement(Checkbox, { checked: checked, onCheckedChange: onCheckedChange }),
|
|
8
|
+
label)));
|
|
9
|
+
};
|
|
10
|
+
export default StacSpatialExtent;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface IStacTemporalExtentProps {
|
|
3
|
+
startTime: Date | undefined;
|
|
4
|
+
setStartTime: (date: Date | undefined) => void;
|
|
5
|
+
endTime: Date | undefined;
|
|
6
|
+
setEndTime: (date: Date | undefined) => void;
|
|
7
|
+
}
|
|
8
|
+
declare function StacTemporalExtent({ startTime, endTime, setStartTime, setEndTime, }: IStacTemporalExtentProps): React.JSX.Element;
|
|
9
|
+
export default StacTemporalExtent;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import SingleDatePicker from '../../../shared/components/SingleDatePicker';
|
|
3
|
+
function StacTemporalExtent({ startTime, endTime, setStartTime, setEndTime, }) {
|
|
4
|
+
return (React.createElement("div", { className: "jgis-stac-filter-extension-section" },
|
|
5
|
+
React.createElement("label", { className: "jgis-stac-filter-extension-label" }, "Temporal Extent"),
|
|
6
|
+
React.createElement(SingleDatePicker, { date: startTime, onDateChange: setStartTime, className: "jgis-stac-datepicker-full-width" }),
|
|
7
|
+
React.createElement(SingleDatePicker, { date: endTime, onDateChange: setEndTime, className: "jgis-stac-datepicker-full-width" })));
|
|
8
|
+
}
|
|
9
|
+
export default StacTemporalExtent;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { IStacItem, IStacPaginationLink, IStacQueryBodyUnion, SetResultsFunction } from "../types/types";
|
|
4
|
+
interface IStacResultsContext {
|
|
5
|
+
results: IStacItem[];
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
totalResults: string;
|
|
8
|
+
totalPages: number;
|
|
9
|
+
handlePaginationClick: (dir: 'next' | 'previous') => Promise<void>;
|
|
10
|
+
handleResultClick: (id: string) => Promise<void>;
|
|
11
|
+
formatResult: (item: IStacItem) => string;
|
|
12
|
+
paginationLinks: IStacPaginationLink[];
|
|
13
|
+
selectedUrl: string;
|
|
14
|
+
setSelectedUrl: (url: string) => void;
|
|
15
|
+
currentPage: number;
|
|
16
|
+
setCurrentPage: (page: number) => void;
|
|
17
|
+
currentPageRef: React.MutableRefObject<number>;
|
|
18
|
+
setResults: SetResultsFunction;
|
|
19
|
+
setIsLoading: (isLoading: boolean) => void;
|
|
20
|
+
setPaginationLinks: (links: IStacPaginationLink[]) => void;
|
|
21
|
+
registerAddToMap: (addFn: (stacData: IStacItem) => void) => void;
|
|
22
|
+
registerHandlePaginationClick: (handleFn: (dir: 'next' | 'previous') => Promise<void>) => void;
|
|
23
|
+
registerBuildQuery: (buildQueryFn: () => IStacQueryBodyUnion) => void;
|
|
24
|
+
executeQuery: (body: IStacQueryBodyUnion, apiUrl?: string, method?: string) => Promise<void>;
|
|
25
|
+
executeQueryWithPage: (pageNumber: number) => Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
interface IStacResultsProviderProps {
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
model?: IJupyterGISModel;
|
|
30
|
+
}
|
|
31
|
+
export declare function StacResultsProvider({ children, model, }: IStacResultsProviderProps): React.JSX.Element;
|
|
32
|
+
export declare function useStacResultsContext(): IStacResultsContext;
|
|
33
|
+
export {};
|