@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.
Files changed (120) hide show
  1. package/lib/commands/BaseCommandIDs.d.ts +2 -0
  2. package/lib/commands/BaseCommandIDs.js +3 -0
  3. package/lib/commands/index.js +66 -0
  4. package/lib/constants.js +4 -0
  5. package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +0 -6
  6. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +2 -2
  7. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +4 -4
  8. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +1 -5
  9. package/lib/formbuilder/formselectors.js +5 -1
  10. package/lib/formbuilder/objectform/StoryEditorForm.d.ts +9 -0
  11. package/lib/formbuilder/objectform/StoryEditorForm.js +16 -0
  12. package/lib/formbuilder/objectform/components/StorySegmentReset.d.ts +8 -0
  13. package/lib/formbuilder/objectform/components/StorySegmentReset.js +24 -0
  14. package/lib/formbuilder/objectform/layer/index.d.ts +1 -0
  15. package/lib/formbuilder/objectform/layer/index.js +1 -0
  16. package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +5 -0
  17. package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +32 -0
  18. package/lib/mainview/mainView.d.ts +18 -0
  19. package/lib/mainview/mainView.js +293 -14
  20. package/lib/panelview/components/layers.d.ts +2 -1
  21. package/lib/panelview/components/layers.js +31 -23
  22. package/lib/panelview/{components/filter-panel → filter-panel}/Filter.js +1 -1
  23. package/lib/panelview/leftpanel.js +89 -7
  24. package/lib/panelview/rightpanel.d.ts +2 -0
  25. package/lib/panelview/rightpanel.js +41 -4
  26. package/lib/panelview/story-maps/PreviewModeSwitch.d.ts +7 -0
  27. package/lib/panelview/story-maps/PreviewModeSwitch.js +13 -0
  28. package/lib/panelview/story-maps/StoryEditorPanel.d.ts +9 -0
  29. package/lib/panelview/story-maps/StoryEditorPanel.js +34 -0
  30. package/lib/panelview/story-maps/StoryNavBar.d.ts +10 -0
  31. package/lib/panelview/story-maps/StoryNavBar.js +11 -0
  32. package/lib/panelview/story-maps/StoryViewerPanel.d.ts +13 -0
  33. package/lib/panelview/story-maps/StoryViewerPanel.js +179 -0
  34. package/lib/panelview/story-maps/components/StoryContentSection.d.ts +6 -0
  35. package/lib/panelview/story-maps/components/StoryContentSection.js +10 -0
  36. package/lib/panelview/story-maps/components/StoryImageSection.d.ts +15 -0
  37. package/lib/panelview/story-maps/components/StoryImageSection.js +13 -0
  38. package/lib/panelview/story-maps/components/StorySubtitleSection.d.ts +11 -0
  39. package/lib/panelview/story-maps/components/StorySubtitleSection.js +9 -0
  40. package/lib/panelview/story-maps/components/StoryTitleSection.d.ts +12 -0
  41. package/lib/panelview/story-maps/components/StoryTitleSection.js +8 -0
  42. package/lib/shared/components/Calendar.d.ts +1 -1
  43. package/lib/shared/components/Combobox.d.ts +21 -0
  44. package/lib/shared/components/Combobox.js +32 -0
  45. package/lib/shared/components/Command.d.ts +18 -0
  46. package/lib/shared/components/Command.js +60 -0
  47. package/lib/shared/components/Dialog.d.ts +15 -0
  48. package/lib/shared/components/Dialog.js +62 -0
  49. package/lib/shared/components/Input.d.ts +3 -0
  50. package/lib/shared/components/Input.js +18 -0
  51. package/lib/shared/components/Pagination.js +3 -2
  52. package/lib/shared/components/RadioGroup.d.ts +5 -0
  53. package/lib/shared/components/RadioGroup.js +26 -0
  54. package/lib/shared/components/Select.d.ts +19 -0
  55. package/lib/shared/components/Select.js +28 -0
  56. package/lib/shared/components/SingleDatePicker.d.ts +11 -0
  57. package/lib/shared/components/SingleDatePicker.js +16 -0
  58. package/lib/shared/components/Switch.d.ts +4 -0
  59. package/lib/shared/components/Switch.js +20 -0
  60. package/lib/stacBrowser/components/StacPanel.d.ts +9 -1
  61. package/lib/stacBrowser/components/StacPanel.js +53 -9
  62. package/lib/stacBrowser/components/filter-extension/QueryableComboBox.d.ts +9 -0
  63. package/lib/stacBrowser/components/filter-extension/QueryableComboBox.js +179 -0
  64. package/lib/stacBrowser/components/filter-extension/QueryableRow.d.ts +16 -0
  65. package/lib/stacBrowser/components/filter-extension/QueryableRow.js +16 -0
  66. package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.d.ts +7 -0
  67. package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.js +49 -0
  68. package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.d.ts +11 -0
  69. package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.js +19 -0
  70. package/lib/stacBrowser/components/{StacFilterSection.d.ts → geodes/StacFilterSection.d.ts} +1 -1
  71. package/lib/stacBrowser/components/{StacFilterSection.js → geodes/StacFilterSection.js} +3 -3
  72. package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.d.ts +7 -0
  73. package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.js +69 -0
  74. package/lib/stacBrowser/components/shared/StacPanelResults.d.ts +3 -0
  75. package/lib/stacBrowser/components/shared/StacPanelResults.js +68 -0
  76. package/lib/stacBrowser/components/shared/StacSpatialExtent.d.ts +8 -0
  77. package/lib/stacBrowser/components/shared/StacSpatialExtent.js +10 -0
  78. package/lib/stacBrowser/components/shared/StacTemporalExtent.d.ts +9 -0
  79. package/lib/stacBrowser/components/shared/StacTemporalExtent.js +9 -0
  80. package/lib/stacBrowser/context/StacResultsContext.d.ts +33 -0
  81. package/lib/stacBrowser/context/StacResultsContext.js +269 -0
  82. package/lib/stacBrowser/hooks/useGeodesSearch.d.ts +24 -0
  83. package/lib/stacBrowser/hooks/useGeodesSearch.js +178 -0
  84. package/lib/stacBrowser/hooks/useStacFilterExtension.d.ts +30 -0
  85. package/lib/stacBrowser/hooks/useStacFilterExtension.js +262 -0
  86. package/lib/stacBrowser/hooks/useStacSearch.d.ts +5 -16
  87. package/lib/stacBrowser/hooks/useStacSearch.js +30 -184
  88. package/lib/stacBrowser/types/types.d.ts +86 -3
  89. package/lib/toolbar/widget.d.ts +15 -0
  90. package/lib/toolbar/widget.js +70 -0
  91. package/lib/tools.d.ts +0 -7
  92. package/lib/tools.js +56 -15
  93. package/package.json +8 -3
  94. package/style/base.css +42 -3
  95. package/style/leftPanel.css +18 -0
  96. package/style/shared/button.css +6 -5
  97. package/style/shared/calendar.css +7 -1
  98. package/style/shared/combobox.css +75 -0
  99. package/style/shared/command.css +178 -0
  100. package/style/shared/dialog.css +177 -0
  101. package/style/shared/input.css +59 -0
  102. package/style/shared/pagination.css +1 -1
  103. package/style/shared/popover.css +1 -0
  104. package/style/shared/radioGroup.css +55 -0
  105. package/style/shared/switch.css +63 -0
  106. package/style/shared/tabs.css +4 -3
  107. package/style/shared/toggle.css +1 -1
  108. package/style/stacBrowser.css +169 -16
  109. package/style/statusBar.css +1 -0
  110. package/style/storyPanel.css +185 -0
  111. package/style/tabPanel.css +1 -88
  112. package/lib/stacBrowser/components/StacPanelFilters.d.ts +0 -14
  113. package/lib/stacBrowser/components/StacPanelFilters.js +0 -81
  114. package/lib/stacBrowser/components/StacPanelResults.d.ts +0 -13
  115. package/lib/stacBrowser/components/StacPanelResults.js +0 -48
  116. /package/lib/panelview/{components/filter-panel → filter-panel}/Filter.d.ts +0 -0
  117. /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.d.ts +0 -0
  118. /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.js +0 -0
  119. /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.d.ts +0 -0
  120. /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;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { DatasetsType, PlatformsType, ProductsType } from "../constants";
2
+ import { DatasetsType, PlatformsType, ProductsType } from "../../constants";
3
3
  type StacFilterSectionProps = {
4
4
  section: 'Collection';
5
5
  data: DatasetsType;
@@ -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 "../../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";
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,3 @@
1
+ import React from 'react';
2
+ declare const StacPanelResults: () => React.JSX.Element;
3
+ export default StacPanelResults;
@@ -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,8 @@
1
+ import React from 'react';
2
+ interface IStacSpatialExtentProps {
3
+ checked: boolean;
4
+ onCheckedChange: (checked: boolean) => void;
5
+ label: string;
6
+ }
7
+ declare const StacSpatialExtent: React.FC<IStacSpatialExtentProps>;
8
+ export default StacSpatialExtent;
@@ -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 {};