@jupytergis/base 0.11.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 (101) hide show
  1. package/lib/commands/BaseCommandIDs.d.ts +1 -0
  2. package/lib/commands/BaseCommandIDs.js +1 -0
  3. package/lib/commands/index.js +52 -0
  4. package/lib/constants.js +3 -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/formbuilder/objectform/StoryEditorForm.d.ts +3 -2
  9. package/lib/formbuilder/objectform/StoryEditorForm.js +7 -1
  10. package/lib/mainview/mainView.d.ts +18 -0
  11. package/lib/mainview/mainView.js +243 -18
  12. package/lib/panelview/{components/filter-panel → filter-panel}/Filter.js +1 -1
  13. package/lib/panelview/leftpanel.js +4 -4
  14. package/lib/panelview/rightpanel.d.ts +2 -0
  15. package/lib/panelview/rightpanel.js +21 -14
  16. package/lib/panelview/{components/story-maps → story-maps}/PreviewModeSwitch.js +3 -2
  17. package/lib/panelview/story-maps/StoryEditorPanel.d.ts +9 -0
  18. package/lib/panelview/story-maps/StoryEditorPanel.js +34 -0
  19. package/lib/panelview/{components/story-maps → story-maps}/StoryNavBar.d.ts +2 -1
  20. package/lib/panelview/{components/story-maps → story-maps}/StoryNavBar.js +3 -3
  21. package/lib/panelview/story-maps/StoryViewerPanel.d.ts +13 -0
  22. package/lib/panelview/{components/story-maps → story-maps}/StoryViewerPanel.js +37 -24
  23. package/lib/panelview/story-maps/components/StoryContentSection.d.ts +6 -0
  24. package/lib/panelview/story-maps/components/StoryContentSection.js +10 -0
  25. package/lib/panelview/story-maps/components/StoryImageSection.d.ts +15 -0
  26. package/lib/panelview/story-maps/components/StoryImageSection.js +13 -0
  27. package/lib/panelview/story-maps/components/StorySubtitleSection.d.ts +11 -0
  28. package/lib/panelview/story-maps/components/StorySubtitleSection.js +9 -0
  29. package/lib/panelview/story-maps/components/StoryTitleSection.d.ts +12 -0
  30. package/lib/panelview/story-maps/components/StoryTitleSection.js +8 -0
  31. package/lib/shared/components/Combobox.d.ts +21 -0
  32. package/lib/shared/components/Combobox.js +32 -0
  33. package/lib/shared/components/Command.js +10 -10
  34. package/lib/shared/components/Input.d.ts +3 -0
  35. package/lib/shared/components/Input.js +18 -0
  36. package/lib/shared/components/Pagination.js +3 -2
  37. package/lib/shared/components/Select.d.ts +19 -0
  38. package/lib/shared/components/Select.js +28 -0
  39. package/lib/shared/components/SingleDatePicker.d.ts +11 -0
  40. package/lib/shared/components/SingleDatePicker.js +16 -0
  41. package/lib/stacBrowser/components/StacPanel.d.ts +9 -1
  42. package/lib/stacBrowser/components/StacPanel.js +53 -9
  43. package/lib/stacBrowser/components/filter-extension/QueryableComboBox.d.ts +9 -0
  44. package/lib/stacBrowser/components/filter-extension/QueryableComboBox.js +179 -0
  45. package/lib/stacBrowser/components/filter-extension/QueryableRow.d.ts +16 -0
  46. package/lib/stacBrowser/components/filter-extension/QueryableRow.js +16 -0
  47. package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.d.ts +7 -0
  48. package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.js +49 -0
  49. package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.d.ts +11 -0
  50. package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.js +19 -0
  51. package/lib/stacBrowser/components/{StacFilterSection.d.ts → geodes/StacFilterSection.d.ts} +1 -1
  52. package/lib/stacBrowser/components/{StacFilterSection.js → geodes/StacFilterSection.js} +3 -3
  53. package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.d.ts +7 -0
  54. package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.js +69 -0
  55. package/lib/stacBrowser/components/shared/StacPanelResults.d.ts +3 -0
  56. package/lib/stacBrowser/components/shared/StacPanelResults.js +68 -0
  57. package/lib/stacBrowser/components/shared/StacSpatialExtent.d.ts +8 -0
  58. package/lib/stacBrowser/components/shared/StacSpatialExtent.js +10 -0
  59. package/lib/stacBrowser/components/shared/StacTemporalExtent.d.ts +9 -0
  60. package/lib/stacBrowser/components/shared/StacTemporalExtent.js +9 -0
  61. package/lib/stacBrowser/context/StacResultsContext.d.ts +33 -0
  62. package/lib/stacBrowser/context/StacResultsContext.js +269 -0
  63. package/lib/stacBrowser/hooks/useGeodesSearch.d.ts +24 -0
  64. package/lib/stacBrowser/hooks/useGeodesSearch.js +178 -0
  65. package/lib/stacBrowser/hooks/useStacFilterExtension.d.ts +30 -0
  66. package/lib/stacBrowser/hooks/useStacFilterExtension.js +262 -0
  67. package/lib/stacBrowser/hooks/useStacSearch.d.ts +5 -16
  68. package/lib/stacBrowser/hooks/useStacSearch.js +30 -184
  69. package/lib/stacBrowser/types/types.d.ts +86 -3
  70. package/lib/toolbar/widget.d.ts +5 -0
  71. package/lib/toolbar/widget.js +23 -2
  72. package/lib/tools.d.ts +0 -7
  73. package/lib/tools.js +55 -14
  74. package/package.json +2 -2
  75. package/style/base.css +38 -3
  76. package/style/shared/button.css +5 -4
  77. package/style/shared/calendar.css +7 -1
  78. package/style/shared/combobox.css +75 -0
  79. package/style/shared/command.css +178 -0
  80. package/style/shared/input.css +59 -0
  81. package/style/shared/pagination.css +1 -1
  82. package/style/shared/popover.css +1 -0
  83. package/style/shared/tabs.css +1 -1
  84. package/style/shared/toggle.css +1 -1
  85. package/style/stacBrowser.css +169 -16
  86. package/style/statusBar.css +1 -0
  87. package/style/storyPanel.css +120 -3
  88. package/style/tabPanel.css +0 -86
  89. package/lib/panelview/components/story-maps/StoryEditorPanel.d.ts +0 -7
  90. package/lib/panelview/components/story-maps/StoryEditorPanel.js +0 -29
  91. package/lib/panelview/components/story-maps/StoryViewerPanel.d.ts +0 -7
  92. package/lib/stacBrowser/components/StacPanelFilters.d.ts +0 -14
  93. package/lib/stacBrowser/components/StacPanelFilters.js +0 -81
  94. package/lib/stacBrowser/components/StacPanelResults.d.ts +0 -13
  95. package/lib/stacBrowser/components/StacPanelResults.js +0 -48
  96. /package/lib/panelview/{components/filter-panel → filter-panel}/Filter.d.ts +0 -0
  97. /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.d.ts +0 -0
  98. /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.js +0 -0
  99. /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.d.ts +0 -0
  100. /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.js +0 -0
  101. /package/lib/panelview/{components/story-maps → story-maps}/PreviewModeSwitch.d.ts +0 -0
@@ -0,0 +1,28 @@
1
+ import { ChevronsUpDownIcon } from 'lucide-react';
2
+ import React, { useState } from 'react';
3
+ import { Button } from "./Button";
4
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "./Command";
5
+ import { Popover, PopoverContent, PopoverTrigger, } from "./Popover";
6
+ import { cn } from './utils';
7
+ export function Select({ items, buttonText, emptyText = 'No option found.', className, buttonClassName, open: controlledOpen, onOpenChange: controlledOnOpenChange, showSearch = false, searchPlaceholder = 'Search...', }) {
8
+ const [internalOpen, setInternalOpen] = useState(false);
9
+ const open = controlledOpen !== undefined ? controlledOpen : internalOpen;
10
+ const setOpen = controlledOnOpenChange || setInternalOpen;
11
+ const handleSelect = (item) => {
12
+ setOpen(false);
13
+ if (item.onSelect) {
14
+ item.onSelect();
15
+ }
16
+ };
17
+ return (React.createElement(Popover, { open: open, onOpenChange: setOpen },
18
+ React.createElement(PopoverTrigger, { asChild: true },
19
+ React.createElement(Button, { variant: "outline", role: "combobox", "aria-expanded": open, className: cn('jgis-combobox-button', buttonClassName) },
20
+ React.createElement("span", { className: "jgis-combobox-button-text" }, buttonText),
21
+ React.createElement(ChevronsUpDownIcon, { className: "jgis-combobox-icon" }))),
22
+ React.createElement(PopoverContent, { className: cn('jgis-select-popover', className) },
23
+ React.createElement(Command, null,
24
+ showSearch && React.createElement(CommandInput, { placeholder: searchPlaceholder }),
25
+ React.createElement(CommandList, null,
26
+ React.createElement(CommandEmpty, null, emptyText),
27
+ React.createElement(CommandGroup, null, items.map(item => (React.createElement(CommandItem, { key: item.value, value: item.label, onSelect: () => handleSelect(item) }, item.label)))))))));
28
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ interface ISingleDatePickerProps {
3
+ date: Date | undefined;
4
+ onDateChange: (date: Date | undefined) => void;
5
+ placeholder?: string;
6
+ className?: string;
7
+ dateFormat?: string;
8
+ showIcon?: boolean;
9
+ }
10
+ declare function SingleDatePicker({ date, onDateChange, placeholder, className, dateFormat, showIcon, }: ISingleDatePickerProps): React.JSX.Element;
11
+ export default SingleDatePicker;
@@ -0,0 +1,16 @@
1
+ import { format } from 'date-fns';
2
+ import { CalendarIcon } from 'lucide-react';
3
+ import React from 'react';
4
+ import { Button } from "./Button";
5
+ import { Calendar } from "./Calendar";
6
+ import { Popover, PopoverContent, PopoverTrigger, } from "./Popover";
7
+ function SingleDatePicker({ date, onDateChange, placeholder = 'Select date', className, dateFormat = 'PPP', showIcon = true, }) {
8
+ return (React.createElement(Popover, null,
9
+ React.createElement(PopoverTrigger, { asChild: true },
10
+ React.createElement(Button, { className: className, variant: "outline" },
11
+ showIcon && React.createElement(CalendarIcon, { className: "jgis-stac-datepicker-icon" }),
12
+ date ? format(date, dateFormat) : React.createElement("span", null, placeholder))),
13
+ React.createElement(PopoverContent, null,
14
+ React.createElement(Calendar, { mode: "single", selected: date, onSelect: onDateChange, autoFocus: true }))));
15
+ }
16
+ export default SingleDatePicker;
@@ -3,5 +3,13 @@ import React from 'react';
3
3
  interface IStacViewProps {
4
4
  model?: IJupyterGISModel;
5
5
  }
6
- declare const StacPanel: ({ model }: IStacViewProps) => React.JSX.Element | null;
6
+ declare const StacPanel: {
7
+ ({ model }: IStacViewProps): React.JSX.Element;
8
+ ProviderSelect: typeof ProviderSelect;
9
+ };
10
+ /**
11
+ * Provider selector component for choosing STAC providers.
12
+ * Uses context to manage selected provider URL.
13
+ */
14
+ declare function ProviderSelect(): React.JSX.Element;
7
15
  export default StacPanel;
@@ -1,20 +1,64 @@
1
1
  import React from 'react';
2
+ import { Select } from "../../shared/components/Select";
2
3
  import { Tabs, TabsContent, TabsList, TabsTrigger, } from "../../shared/components/Tabs";
3
- import useStacSearch from "../hooks/useStacSearch";
4
- import StacPanelFilters from './StacPanelFilters';
5
- import StacPanelResults from './StacPanelResults';
6
- const StacPanel = ({ model }) => {
7
- const { filterState, filterSetters, results, startTime, setStartTime, endTime, setEndTime, totalPages, currentPage, totalResults, handlePaginationClick, handleResultClick, formatResult, isLoading, useWorldBBox, setUseWorldBBox, } = useStacSearch({ model });
4
+ import StacFilterExtensionPanel from "./filter-extension/StacFilterExtensionPanel";
5
+ import StacGeodesFilterPanel from "./geodes/StacGeodesFilterPanel";
6
+ import StacPanelResults from "./shared/StacPanelResults";
7
+ import { StacResultsProvider, useStacResultsContext, } from "../context/StacResultsContext";
8
+ const GEODES_URL = 'https://geodes-portal.cnes.fr/api/stac/';
9
+ const COPERNICUS_URL = 'https://stac.dataspace.copernicus.eu/v1/';
10
+ const WORLDPOP_URL = 'https://api.stac.worldpop.org/';
11
+ const PROVIDERS = [
12
+ { url: COPERNICUS_URL, name: 'Copernicus' },
13
+ { url: GEODES_URL, name: 'GEODES' },
14
+ { url: WORLDPOP_URL, name: 'WorldPop' },
15
+ ];
16
+ // URL to panel component mapping for extensibility
17
+ // Add new entries here to support additional STAC providers
18
+ const URL_TO_PANEL_MAP = {
19
+ [GEODES_URL]: StacGeodesFilterPanel,
20
+ };
21
+ // Inner component that uses the context
22
+ const StacPanelContent = ({ model }) => {
23
+ var _a;
24
+ const { totalResults, selectedUrl } = useStacResultsContext();
8
25
  if (!model) {
9
26
  return null;
10
27
  }
11
- return (React.createElement(Tabs, { defaultValue: "filters", className: "jgis-panel-tabs" },
12
- React.createElement(TabsList, { style: { borderRadius: 0 } },
28
+ const ProviderPanel = (_a = URL_TO_PANEL_MAP[selectedUrl]) !== null && _a !== void 0 ? _a : StacFilterExtensionPanel;
29
+ return (React.createElement(Tabs, { defaultValue: "filters", className: "jgis-panel-tabs", style: { boxShadow: 'none' } },
30
+ React.createElement(TabsList, { className: "jgis-stac-panel-tabs-list" },
13
31
  React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", value: "filters" }, "Filters"),
14
32
  React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", value: "results" }, `Results (${totalResults})`)),
15
33
  React.createElement(TabsContent, { value: "filters" },
16
- React.createElement(StacPanelFilters, { filterState: filterState, filterSetters: filterSetters, startTime: startTime, setStartTime: setStartTime, endTime: endTime, setEndTime: setEndTime, useWorldBBox: useWorldBBox, setUseWorldBBox: setUseWorldBBox })),
34
+ React.createElement("div", { className: "jgis-stac-filter-extension-panel" },
35
+ React.createElement(StacPanel.ProviderSelect, null),
36
+ selectedUrl ? (React.createElement(ProviderPanel, { model: model })) : (React.createElement("div", { className: "jgis-stac-panel-placeholder" }, "Please select a provider above")))),
17
37
  React.createElement(TabsContent, { value: "results" },
18
- React.createElement(StacPanelResults, { results: results, currentPage: currentPage, totalPages: totalPages, handlePaginationClick: handlePaginationClick, handleResultClick: handleResultClick, formatResult: formatResult, isLoading: isLoading }))));
38
+ React.createElement(StacPanelResults, null))));
39
+ };
40
+ // Outer component that provides the context
41
+ const StacPanel = ({ model }) => {
42
+ return (React.createElement(StacResultsProvider, { model: model },
43
+ React.createElement(StacPanelContent, { model: model })));
19
44
  };
45
+ /**
46
+ * Provider selector component for choosing STAC providers.
47
+ * Uses context to manage selected provider URL.
48
+ */
49
+ function ProviderSelect() {
50
+ const { selectedUrl, setSelectedUrl } = useStacResultsContext();
51
+ const selectedProvider = PROVIDERS.find(provider => provider.url === selectedUrl);
52
+ const buttonText = (selectedProvider === null || selectedProvider === void 0 ? void 0 : selectedProvider.name) || 'Select a provider...';
53
+ const items = PROVIDERS.map(provider => ({
54
+ value: provider.url,
55
+ label: provider.name,
56
+ onSelect: () => setSelectedUrl(provider.url),
57
+ }));
58
+ return (React.createElement("div", { className: "jgis-stac-filter-extension-section" },
59
+ React.createElement("label", { className: "jgis-stac-filter-extension-label" }, "Provider"),
60
+ React.createElement(Select, { items: items, buttonText: buttonText, emptyText: "No provider found.", buttonClassName: "jgis-stac-filter-extension-select" })));
61
+ }
62
+ // Attach ProviderSelect as a composable sub-component
63
+ StacPanel.ProviderSelect = ProviderSelect;
20
64
  export default StacPanel;
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { IQueryableFilter, IStacQueryables, UpdateSelectedQueryables } from "../../types/types";
3
+ interface IQueryableComboProps {
4
+ queryables: IStacQueryables;
5
+ selectedQueryables: Record<string, IQueryableFilter>;
6
+ updateSelectedQueryables: UpdateSelectedQueryables;
7
+ }
8
+ export declare function QueryableComboBox({ queryables, selectedQueryables, updateSelectedQueryables, }: IQueryableComboProps): React.JSX.Element;
9
+ export {};
@@ -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;