nova-trame 1.0.1__py3-none-any.whl → 1.1.1__py3-none-any.whl
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.
Potentially problematic release.
This version of nova-trame might be problematic. Click here for more details.
- nova/trame/model/data_selector.py +46 -4
- nova/trame/view/components/data_selector.py +115 -46
- nova/trame/view/components/input_field.py +7 -2
- nova/trame/view/components/ornl/neutron_data_selector.py +4 -0
- nova/trame/view/layouts/grid.py +7 -1
- nova/trame/view/layouts/hbox.py +7 -1
- nova/trame/view/layouts/vbox.py +7 -1
- nova/trame/view/theme/assets/core_style.scss +25 -1
- nova/trame/view/theme/assets/favicon.png +0 -0
- nova/trame/view/theme/assets/js/revo_grid.js +13 -5
- nova/trame/view_model/data_selector.py +11 -1
- nova/trame/view_model/ornl/neutron_data_selector.py +2 -0
- {nova_trame-1.0.1.dist-info → nova_trame-1.1.1.dist-info}/METADATA +1 -1
- {nova_trame-1.0.1.dist-info → nova_trame-1.1.1.dist-info}/RECORD +17 -17
- {nova_trame-1.0.1.dist-info → nova_trame-1.1.1.dist-info}/WHEEL +0 -0
- {nova_trame-1.0.1.dist-info → nova_trame-1.1.1.dist-info}/entry_points.txt +0 -0
- {nova_trame-1.0.1.dist-info → nova_trame-1.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any, Dict, List, Optional
|
|
5
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
6
6
|
|
|
7
7
|
from natsort import natsorted
|
|
8
8
|
from pydantic import BaseModel, Field
|
|
@@ -13,6 +13,11 @@ class DataSelectorState(BaseModel, validate_assignment=True):
|
|
|
13
13
|
|
|
14
14
|
directory: str = Field(default="")
|
|
15
15
|
extensions: List[str] = Field(default=[])
|
|
16
|
+
search: str = Field(default="", title="Search")
|
|
17
|
+
# True: A->Z, False: Z->A, None: no order
|
|
18
|
+
sort_alpha: Optional[bool] = Field(default=None)
|
|
19
|
+
# True: Recent modifications first, False: Older modifications first, None: no order
|
|
20
|
+
sort_time: Optional[bool] = Field(default=None)
|
|
16
21
|
subdirectory: str = Field(default="")
|
|
17
22
|
|
|
18
23
|
|
|
@@ -95,18 +100,25 @@ class DataSelectorModel:
|
|
|
95
100
|
datafile_path = base_path / self.state.subdirectory
|
|
96
101
|
|
|
97
102
|
for entry in os.scandir(datafile_path):
|
|
103
|
+
can_add = False
|
|
98
104
|
if entry.is_file():
|
|
99
105
|
if self.state.extensions:
|
|
100
106
|
for extension in self.state.extensions:
|
|
101
107
|
if entry.path.lower().endswith(extension):
|
|
102
|
-
|
|
108
|
+
can_add = True
|
|
103
109
|
break
|
|
104
110
|
else:
|
|
105
|
-
|
|
111
|
+
can_add = True
|
|
112
|
+
|
|
113
|
+
if self.state.search and self.state.search.lower() not in entry.path.lower():
|
|
114
|
+
can_add = False
|
|
115
|
+
|
|
116
|
+
if can_add:
|
|
117
|
+
datafiles.append((entry.path, entry.stat().st_mtime))
|
|
106
118
|
except OSError:
|
|
107
119
|
pass
|
|
108
120
|
|
|
109
|
-
return
|
|
121
|
+
return self.sort_datafiles(datafiles)
|
|
110
122
|
|
|
111
123
|
def get_datafiles(self) -> List[str]:
|
|
112
124
|
base_path = Path(self.state.directory)
|
|
@@ -115,3 +127,33 @@ class DataSelectorModel:
|
|
|
115
127
|
|
|
116
128
|
def set_subdirectory(self, subdirectory_path: str) -> None:
|
|
117
129
|
self.state.subdirectory = subdirectory_path
|
|
130
|
+
|
|
131
|
+
def sort_datafiles(self, files: List[Tuple[str, float]]) -> List[str]:
|
|
132
|
+
if self.state.sort_alpha is not None:
|
|
133
|
+
files = natsorted(files, key=lambda x: x[0].lower(), reverse=not self.state.sort_alpha)
|
|
134
|
+
elif self.state.sort_time is not None:
|
|
135
|
+
files = sorted(files, key=lambda x: x[1], reverse=self.state.sort_time)
|
|
136
|
+
|
|
137
|
+
return [file[0] for file in files]
|
|
138
|
+
|
|
139
|
+
def toggle_alpha_sort(self) -> None:
|
|
140
|
+
# Reset the time sort since we've changed alpha sort more recently
|
|
141
|
+
self.state.sort_time = None
|
|
142
|
+
|
|
143
|
+
if self.state.sort_alpha is None:
|
|
144
|
+
self.state.sort_alpha = True
|
|
145
|
+
elif self.state.sort_alpha:
|
|
146
|
+
self.state.sort_alpha = False
|
|
147
|
+
else:
|
|
148
|
+
self.state.sort_alpha = None
|
|
149
|
+
|
|
150
|
+
def toggle_time_sort(self) -> None:
|
|
151
|
+
# Reset the alpha sort since we've changed time sort more recently
|
|
152
|
+
self.state.sort_alpha = None
|
|
153
|
+
|
|
154
|
+
if self.state.sort_time is None:
|
|
155
|
+
self.state.sort_time = True
|
|
156
|
+
elif self.state.sort_time:
|
|
157
|
+
self.state.sort_time = False
|
|
158
|
+
else:
|
|
159
|
+
self.state.sort_time = None
|
|
@@ -11,7 +11,7 @@ from trame_server.core import State
|
|
|
11
11
|
|
|
12
12
|
from nova.mvvm._internal.utils import rgetdictvalue
|
|
13
13
|
from nova.mvvm.trame_binding import TrameBinding
|
|
14
|
-
from nova.trame._internal.utils import get_state_param, set_state_param
|
|
14
|
+
from nova.trame._internal.utils import get_state_name, get_state_param, set_state_param
|
|
15
15
|
from nova.trame.model.data_selector import DataSelectorModel, DataSelectorState
|
|
16
16
|
from nova.trame.view.layouts import GridLayout, HBoxLayout, VBoxLayout
|
|
17
17
|
from nova.trame.view_model.data_selector import DataSelectorViewModel
|
|
@@ -28,6 +28,7 @@ class DataSelector(datagrid.VGrid):
|
|
|
28
28
|
self,
|
|
29
29
|
v_model: Union[str, Tuple],
|
|
30
30
|
directory: Union[str, Tuple],
|
|
31
|
+
clear_selection_on_directory_change: Union[bool, Tuple] = True,
|
|
31
32
|
extensions: Union[List[str], Tuple, None] = None,
|
|
32
33
|
prefix: Union[str, Tuple] = "",
|
|
33
34
|
subdirectory: Union[str, Tuple] = "",
|
|
@@ -45,6 +46,8 @@ class DataSelector(datagrid.VGrid):
|
|
|
45
46
|
directory : Union[str, Tuple]
|
|
46
47
|
The top-level folder to expose to users. Only contents of this directory and its children will be exposed to
|
|
47
48
|
users.
|
|
49
|
+
clear_selection_on_directory_change: Union[bool, Tuple], optional
|
|
50
|
+
Whether or not to clear the selected files when the directory is changed.
|
|
48
51
|
extensions : Union[List[str], Tuple], optional
|
|
49
52
|
A list of file extensions to restrict selection to. If unset, then all files will be shown.
|
|
50
53
|
prefix : Union[str, Tuple], optional
|
|
@@ -96,6 +99,7 @@ class DataSelector(datagrid.VGrid):
|
|
|
96
99
|
else:
|
|
97
100
|
self._v_model_name_in_state = v_model[0].split(".")[0]
|
|
98
101
|
|
|
102
|
+
self._clear_selection = clear_selection_on_directory_change
|
|
99
103
|
self._directory = directory
|
|
100
104
|
self._last_directory = get_state_param(self.state, self._directory)
|
|
101
105
|
self._extensions = extensions if extensions is not None else []
|
|
@@ -129,6 +133,8 @@ class DataSelector(datagrid.VGrid):
|
|
|
129
133
|
return get_server(None, client_type="vue3").state
|
|
130
134
|
|
|
131
135
|
def create_ui(self, *args: Any, **kwargs: Any) -> None:
|
|
136
|
+
show_directories = isinstance(self._subdirectory, tuple) or not self._subdirectory
|
|
137
|
+
|
|
132
138
|
with VBoxLayout(classes="nova-data-selector", stretch=True) as self._layout:
|
|
133
139
|
with HBoxLayout(valign="center"):
|
|
134
140
|
self._layout.filter = html.Div(classes="flex-1-1")
|
|
@@ -139,7 +145,7 @@ class DataSelector(datagrid.VGrid):
|
|
|
139
145
|
vuetify.VTooltip("Refresh Contents", activator="parent")
|
|
140
146
|
|
|
141
147
|
with GridLayout(columns=2, stretch=True):
|
|
142
|
-
if
|
|
148
|
+
if show_directories:
|
|
143
149
|
with VBoxLayout(stretch=True):
|
|
144
150
|
vuetify.VListSubheader("Available Directories", classes="flex-0-1 justify-center px-0")
|
|
145
151
|
vuetify.VTreeview(
|
|
@@ -155,51 +161,113 @@ class DataSelector(datagrid.VGrid):
|
|
|
155
161
|
)
|
|
156
162
|
vuetify.VListItem("No directories found", classes="flex-0-1 text-center", v_else=True)
|
|
157
163
|
|
|
158
|
-
if
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
164
|
+
with VBoxLayout(column_span=1 if show_directories else 2, stretch=True):
|
|
165
|
+
with VBoxLayout(classes="mx-2", gap="0.5em"):
|
|
166
|
+
with HBoxLayout(gap="0.25em", valign="center"):
|
|
167
|
+
if isinstance(self._extensions, tuple):
|
|
168
|
+
extensions_name = f"{get_state_name(self._extensions[0])}.extensions"
|
|
169
|
+
else:
|
|
170
|
+
extensions_name = f"{self._state_name}.extensions"
|
|
171
|
+
|
|
172
|
+
InputField(v_model=f"{self._state_name}.search")
|
|
173
|
+
with vuetify.VBtn(classes="icon-btn", icon=True, click=self._vm.toggle_alpha_sort):
|
|
174
|
+
vuetify.VTooltip(
|
|
175
|
+
"Sorting A->Z",
|
|
176
|
+
activator="parent",
|
|
177
|
+
v_if=f"{self._state_name}.sort_alpha === true",
|
|
178
|
+
)
|
|
179
|
+
vuetify.VTooltip(
|
|
180
|
+
"Sorting Z->A",
|
|
181
|
+
activator="parent",
|
|
182
|
+
v_else_if=f"{self._state_name}.sort_alpha === false",
|
|
183
|
+
)
|
|
184
|
+
vuetify.VTooltip("Click to sort alphanumerically", activator="parent", v_else=True)
|
|
185
|
+
|
|
186
|
+
vuetify.VIcon(
|
|
187
|
+
"mdi-sort-alphabetical-ascending",
|
|
188
|
+
size=16,
|
|
189
|
+
v_if=f"{self._state_name}.sort_alpha === true",
|
|
190
|
+
)
|
|
191
|
+
vuetify.VIcon(
|
|
192
|
+
"mdi-sort-alphabetical-descending",
|
|
193
|
+
size=16,
|
|
194
|
+
v_else_if=f"{self._state_name}.sort_alpha === false",
|
|
195
|
+
)
|
|
196
|
+
vuetify.VIcon("mdi-order-alphabetical-ascending", size=16, v_else=True)
|
|
197
|
+
with vuetify.VBtn(classes="icon-btn", icon=True, click=self._vm.toggle_time_sort):
|
|
198
|
+
vuetify.VTooltip(
|
|
199
|
+
"Newest modification times first",
|
|
200
|
+
activator="parent",
|
|
201
|
+
v_if=f"{self._state_name}.sort_time === true",
|
|
202
|
+
)
|
|
203
|
+
vuetify.VTooltip(
|
|
204
|
+
"Oldest modification times first",
|
|
205
|
+
activator="parent",
|
|
206
|
+
v_else_if=f"{self._state_name}.sort_time === false",
|
|
207
|
+
)
|
|
208
|
+
vuetify.VTooltip("Click to sort by modification times", activator="parent", v_else=True)
|
|
209
|
+
|
|
210
|
+
vuetify.VIcon(
|
|
211
|
+
"mdi-sort-clock-ascending",
|
|
212
|
+
size=16,
|
|
213
|
+
v_if=f"{self._state_name}.sort_time === true",
|
|
214
|
+
)
|
|
215
|
+
vuetify.VIcon(
|
|
216
|
+
"mdi-sort-clock-descending",
|
|
217
|
+
size=16,
|
|
218
|
+
v_else_if=f"{self._state_name}.sort_time === false",
|
|
219
|
+
)
|
|
220
|
+
vuetify.VIcon("mdi-clock", size=16, v_else=True)
|
|
221
|
+
|
|
222
|
+
html.P(
|
|
223
|
+
f"Showing {{{{ {extensions_name}.join(',') }}}} files",
|
|
224
|
+
v_if=f"{extensions_name}.length > 0",
|
|
225
|
+
)
|
|
171
226
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
**kwargs,
|
|
185
|
-
)
|
|
186
|
-
if self._label:
|
|
187
|
-
self.label = self._label
|
|
188
|
-
if "update_modelValue" not in kwargs:
|
|
189
|
-
self.update_modelValue = self._flush_state
|
|
190
|
-
|
|
191
|
-
# Sets up some JavaScript event handlers when the component is mounted.
|
|
192
|
-
with self:
|
|
193
|
-
client.ClientTriggers(
|
|
194
|
-
mounted=(
|
|
195
|
-
"window.grid_manager.add("
|
|
196
|
-
f" '{self._revogrid_id}',"
|
|
197
|
-
f" '{self._v_model}',"
|
|
198
|
-
f" '{self._datafiles_name}',"
|
|
199
|
-
f" '{self._v_model_name_in_state}'"
|
|
200
|
-
")"
|
|
227
|
+
if "columns" in kwargs:
|
|
228
|
+
columns = kwargs.pop("columns")
|
|
229
|
+
else:
|
|
230
|
+
columns = (
|
|
231
|
+
"[{"
|
|
232
|
+
" cellTemplate: (createElement, props) =>"
|
|
233
|
+
f" window.grid_manager.get('{self._revogrid_id}').cellTemplate(createElement, props),"
|
|
234
|
+
" columnTemplate: (createElement) =>"
|
|
235
|
+
f" window.grid_manager.get('{self._revogrid_id}').columnTemplate(createElement),"
|
|
236
|
+
" name: 'Available Datafiles',"
|
|
237
|
+
" prop: 'title',"
|
|
238
|
+
"}]",
|
|
201
239
|
)
|
|
240
|
+
|
|
241
|
+
super().__init__(
|
|
242
|
+
v_model=self._v_model,
|
|
243
|
+
can_focus=False,
|
|
244
|
+
columns=columns,
|
|
245
|
+
frame_size=10,
|
|
246
|
+
hide_attribution=True,
|
|
247
|
+
id=self._revogrid_id,
|
|
248
|
+
readonly=True,
|
|
249
|
+
stretch=True,
|
|
250
|
+
source=(self._datafiles_name,),
|
|
251
|
+
theme="compact",
|
|
252
|
+
**kwargs,
|
|
202
253
|
)
|
|
254
|
+
if self._label:
|
|
255
|
+
self.label = self._label
|
|
256
|
+
if "update_modelValue" not in kwargs:
|
|
257
|
+
self.update_modelValue = self._flush_state
|
|
258
|
+
|
|
259
|
+
# Sets up some JavaScript event handlers when the component is mounted.
|
|
260
|
+
with self:
|
|
261
|
+
client.ClientTriggers(
|
|
262
|
+
mounted=(
|
|
263
|
+
"window.grid_manager.add("
|
|
264
|
+
f" '{self._revogrid_id}',"
|
|
265
|
+
f" '{self._v_model}',"
|
|
266
|
+
f" '{self._datafiles_name}',"
|
|
267
|
+
f" '{self._v_model_name_in_state}'"
|
|
268
|
+
")"
|
|
269
|
+
)
|
|
270
|
+
)
|
|
203
271
|
|
|
204
272
|
with cast(
|
|
205
273
|
vuetify.VSelect,
|
|
@@ -240,8 +308,9 @@ class DataSelector(datagrid.VGrid):
|
|
|
240
308
|
self._vm.update_view(refresh_directories=True)
|
|
241
309
|
|
|
242
310
|
def reset(self, _: Any = None) -> None:
|
|
243
|
-
self.
|
|
244
|
-
|
|
311
|
+
if bool(get_state_param(self.state, self._clear_selection)):
|
|
312
|
+
self._reset_state()
|
|
313
|
+
self._reset_rv_grid()
|
|
245
314
|
|
|
246
315
|
def set_subdirectory(self, subdirectory_path: str = "") -> None:
|
|
247
316
|
set_state_param(self.state, self._subdirectory, subdirectory_path)
|
|
@@ -170,7 +170,8 @@ class InputField:
|
|
|
170
170
|
- file
|
|
171
171
|
- input
|
|
172
172
|
- otp
|
|
173
|
-
- radio
|
|
173
|
+
- radio - Produces a radio button group. Note that this accepts an additional parameter items that expects \
|
|
174
|
+
a list of dictionaries with the following format: { title: 'Item 1', value: 'item_1' }.
|
|
174
175
|
- range-slider
|
|
175
176
|
- select - Produces a dropdown menu. This menu can have items automatically populated if the v_model is \
|
|
176
177
|
connected to a Pydantic field that uses an Enum type. Otherwise, you must specify the items parameter \
|
|
@@ -244,7 +245,11 @@ class InputField:
|
|
|
244
245
|
case "otp":
|
|
245
246
|
input = vuetify.VOtpInput(**kwargs)
|
|
246
247
|
case "radio":
|
|
247
|
-
|
|
248
|
+
items = kwargs.pop("items", None)
|
|
249
|
+
if isinstance(items, tuple):
|
|
250
|
+
items = items[0]
|
|
251
|
+
with vuetify.VRadioGroup(**kwargs) as input:
|
|
252
|
+
vuetify.VRadio(v_for=f"item in {items}", label=("item.title",), value=("item.value",))
|
|
248
253
|
case "range-slider":
|
|
249
254
|
input = vuetify.VRangeSlider(**kwargs)
|
|
250
255
|
case "select":
|
|
@@ -31,6 +31,7 @@ class NeutronDataSelector(DataSelector):
|
|
|
31
31
|
self,
|
|
32
32
|
v_model: Union[str, Tuple],
|
|
33
33
|
allow_custom_directories: Union[bool, Tuple] = False,
|
|
34
|
+
clear_selection_on_experiment_change: Union[bool, Tuple] = True,
|
|
34
35
|
data_source: Literal["filesystem", "oncat"] = "filesystem",
|
|
35
36
|
facility: Union[str, Tuple] = "",
|
|
36
37
|
instrument: Union[str, Tuple] = "",
|
|
@@ -52,6 +53,8 @@ class NeutronDataSelector(DataSelector):
|
|
|
52
53
|
allow_custom_directories : Union[bool, Tuple], optional
|
|
53
54
|
Whether or not to allow users to provide their own directories to search for datafiles in. Ignored if the
|
|
54
55
|
facility parameter is set.
|
|
56
|
+
clear_selection_on_experiment_change: Union[bool, Tuple], optional
|
|
57
|
+
Whether or not to clear the selected files when the user changes the facility, instrument, or experiment.
|
|
55
58
|
data_source : Literal["filesystem", "oncat"], optional
|
|
56
59
|
The source from which to pull datafiles. Defaults to "filesystem". If using ONCat, you will need to set the
|
|
57
60
|
following environment variables for local development: `ONCAT_CLIENT_ID` and `ONCAT_CLIENT_SECRET`. Note
|
|
@@ -119,6 +122,7 @@ class NeutronDataSelector(DataSelector):
|
|
|
119
122
|
super().__init__(
|
|
120
123
|
v_model,
|
|
121
124
|
"",
|
|
125
|
+
clear_selection_on_directory_change=clear_selection_on_experiment_change,
|
|
122
126
|
extensions=extensions,
|
|
123
127
|
subdirectory=subdirectory if data_source == "filesystem" else "oncat",
|
|
124
128
|
refresh_rate=refresh_rate,
|
nova/trame/view/layouts/grid.py
CHANGED
|
@@ -74,7 +74,7 @@ class GridLayout(html.Div):
|
|
|
74
74
|
classes = kwargs.pop("classes", [])
|
|
75
75
|
if isinstance(classes, list):
|
|
76
76
|
classes = " ".join(classes)
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
if stretch:
|
|
79
79
|
if valign:
|
|
80
80
|
warn("Ignoring valign parameter to GridLayout since stretch=True.", stacklevel=1)
|
|
@@ -83,6 +83,12 @@ class GridLayout(html.Div):
|
|
|
83
83
|
else:
|
|
84
84
|
classes += " flex-0-1"
|
|
85
85
|
|
|
86
|
+
v_show = kwargs.get("v_show", None)
|
|
87
|
+
if v_show:
|
|
88
|
+
classes = (f"{v_show} ? '{classes} d-grid' : '{classes}'",)
|
|
89
|
+
else:
|
|
90
|
+
classes += " d-grid"
|
|
91
|
+
|
|
86
92
|
widget_style = self.get_root_styles(columns, height, width, halign, valign, gap)
|
|
87
93
|
user_style = kwargs.pop("style", {})
|
|
88
94
|
|
nova/trame/view/layouts/hbox.py
CHANGED
|
@@ -63,12 +63,18 @@ class HBoxLayout(html.Div):
|
|
|
63
63
|
classes = kwargs.pop("classes", [])
|
|
64
64
|
if isinstance(classes, list):
|
|
65
65
|
classes = " ".join(classes)
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
if stretch:
|
|
68
68
|
classes += " flex-1-1 overflow-y-auto"
|
|
69
69
|
else:
|
|
70
70
|
classes += " flex-0-1"
|
|
71
71
|
|
|
72
|
+
v_show = kwargs.get("v_show", None)
|
|
73
|
+
if v_show:
|
|
74
|
+
classes = (f"{v_show} ? '{classes} d-flex flex-row' : '{classes}'",)
|
|
75
|
+
else:
|
|
76
|
+
classes += " d-flex flex-row"
|
|
77
|
+
|
|
72
78
|
widget_style = self.get_root_styles(height, width, halign, valign, gap, vspace)
|
|
73
79
|
user_style = kwargs.pop("style", {})
|
|
74
80
|
|
nova/trame/view/layouts/vbox.py
CHANGED
|
@@ -63,12 +63,18 @@ class VBoxLayout(html.Div):
|
|
|
63
63
|
classes = kwargs.pop("classes", [])
|
|
64
64
|
if isinstance(classes, list):
|
|
65
65
|
classes = " ".join(classes)
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
if stretch:
|
|
68
68
|
classes += " flex-1-1 overflow-y-auto"
|
|
69
69
|
else:
|
|
70
70
|
classes += " flex-0-1"
|
|
71
71
|
|
|
72
|
+
v_show = kwargs.get("v_show", None)
|
|
73
|
+
if v_show:
|
|
74
|
+
classes = (f"{v_show} ? '{classes} d-flex flex-column' : '{classes}'",)
|
|
75
|
+
else:
|
|
76
|
+
classes += " d-flex flex-column"
|
|
77
|
+
|
|
72
78
|
widget_style = self.get_root_styles(height, width, halign, valign, gap, vspace)
|
|
73
79
|
user_style = kwargs.pop("style", {})
|
|
74
80
|
|
|
@@ -58,11 +58,27 @@ html {
|
|
|
58
58
|
cursor: pointer;
|
|
59
59
|
margin-right: 0.25em;
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
p {
|
|
63
|
+
line-height: 1;
|
|
64
|
+
margin-top: 1em;
|
|
65
|
+
white-space: normal;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.rgCell label {
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.rv-row-text {
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
white-space: nowrap;
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
.header-content {
|
|
64
80
|
font-weight: 500;
|
|
65
|
-
height:
|
|
81
|
+
height: fit-content;
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
.inner-content-table {
|
|
@@ -257,6 +273,14 @@ html {
|
|
|
257
273
|
}
|
|
258
274
|
}
|
|
259
275
|
}
|
|
276
|
+
|
|
277
|
+
.v-treeview.hide-actions .v-list-item .v-list-item__prepend {
|
|
278
|
+
width: unset;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.nova-data-selector .icon-btn {
|
|
282
|
+
height: 28px;
|
|
283
|
+
}
|
|
260
284
|
}
|
|
261
285
|
|
|
262
286
|
|
|
Binary file
|
|
@@ -104,16 +104,18 @@ class RevoGrid {
|
|
|
104
104
|
},
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
const spanNode = createElement('span', {'class': 'cursor-pointer rv-row-text'}, props.model[props.prop])
|
|
108
|
+
|
|
109
|
+
return createElement('label', { 'title': props.model[props.prop] }, inputVNode, spanNode)
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
columnTemplate(createElement) {
|
|
113
|
+
const trameState = window.trame.state.state
|
|
114
|
+
const availableData = _.get(trameState, this.dataKey)
|
|
115
|
+
|
|
111
116
|
const inputVNode = createElement('input', {
|
|
112
117
|
type: 'checkbox',
|
|
113
118
|
onChange: (e) => {
|
|
114
|
-
const trameState = window.trame.state.state
|
|
115
|
-
const availableData = _.get(trameState, this.dataKey)
|
|
116
|
-
|
|
117
119
|
if (e.target.checked) {
|
|
118
120
|
_.set(trameState, this.modelKey, availableData.map((item) => item.path))
|
|
119
121
|
} else {
|
|
@@ -125,8 +127,14 @@ class RevoGrid {
|
|
|
125
127
|
window.trame.state.dirty(this.stateKey)
|
|
126
128
|
},
|
|
127
129
|
})
|
|
130
|
+
const header = createElement('div', {'class': 'd-flex'}, inputVNode, 'Available Datafiles')
|
|
131
|
+
|
|
132
|
+
let controls = null
|
|
133
|
+
if (availableData.length < 1) {
|
|
134
|
+
controls = createElement('p', {}, 'No files found. Select a directory with files on the left.')
|
|
135
|
+
}
|
|
128
136
|
|
|
129
|
-
return
|
|
137
|
+
return createElement('div', {'class': 'd-flex flex-column'}, header, controls)
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
initShiftKeyListeners() {
|
|
@@ -56,7 +56,9 @@ class DataSelectorViewModel:
|
|
|
56
56
|
self.expand_directory(paths)
|
|
57
57
|
|
|
58
58
|
def on_state_updated(self, results: Dict[str, Any]) -> None:
|
|
59
|
-
|
|
59
|
+
for result in results.get("updated", []):
|
|
60
|
+
if result == "search":
|
|
61
|
+
self.update_view()
|
|
60
62
|
|
|
61
63
|
def set_binding_parameters(self, **kwargs: Any) -> None:
|
|
62
64
|
self.model.set_binding_parameters(**kwargs)
|
|
@@ -66,6 +68,14 @@ class DataSelectorViewModel:
|
|
|
66
68
|
self.model.set_subdirectory(subdirectory_path)
|
|
67
69
|
self.update_view()
|
|
68
70
|
|
|
71
|
+
def toggle_alpha_sort(self) -> None:
|
|
72
|
+
self.model.toggle_alpha_sort()
|
|
73
|
+
self.update_view()
|
|
74
|
+
|
|
75
|
+
def toggle_time_sort(self) -> None:
|
|
76
|
+
self.model.toggle_time_sort()
|
|
77
|
+
self.update_view()
|
|
78
|
+
|
|
69
79
|
def transform_datafiles(self, datafiles: List[Any]) -> List[Dict[str, str]]:
|
|
70
80
|
return [{"path": datafile, "title": os.path.basename(datafile)} for datafile in datafiles]
|
|
71
81
|
|
|
@@ -33,6 +33,8 @@ class NeutronDataSelectorViewModel(DataSelectorViewModel):
|
|
|
33
33
|
case "custom_directory":
|
|
34
34
|
self.reset()
|
|
35
35
|
self.update_view()
|
|
36
|
+
case "search":
|
|
37
|
+
self.update_view()
|
|
36
38
|
|
|
37
39
|
def transform_datafiles(self, datafiles: List[Any]) -> List[Dict[str, str]]:
|
|
38
40
|
return [{"title": os.path.basename(datafile["path"]), **datafile} for datafile in datafiles]
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
nova/__init__.py,sha256=ED6jHcYiuYpr_0vjGz0zx2lrrmJT9sDJCzIljoDfmlM,65
|
|
2
2
|
nova/trame/__init__.py,sha256=gFrAg1qva5PIqR5TjvPzAxLx103IKipJLqp3XXvrQL8,59
|
|
3
3
|
nova/trame/_internal/utils.py,sha256=lTTJnfqbbIe21Tg2buf5MXqKUEUop7Va5PZgpWMzRkI,1381
|
|
4
|
-
nova/trame/model/data_selector.py,sha256=
|
|
4
|
+
nova/trame/model/data_selector.py,sha256=_pGKvKoz9QheY4Zyi5dy9XjHuo75MoiN1J928J09dSo,6180
|
|
5
5
|
nova/trame/model/ornl/analysis_data_selector.py,sha256=P7IEJdqCAUsEOCof4c2JantPXd9vz0EtvhryEKvscbw,5544
|
|
6
6
|
nova/trame/model/ornl/neutron_data_selector.py,sha256=YMoNEpDKgjP_y18oYj-N9IjkxtqwHz9JYMlURQA4BCE,2148
|
|
7
7
|
nova/trame/model/ornl/oncat_data_selector.py,sha256=3JEkWGMU-esWA9DUTglju9hEP9LyZ7EUXLj1yO5BIDs,4755
|
|
8
8
|
nova/trame/model/remote_file_input.py,sha256=eAk7ZsFgNKcnpJ6KmOQDhiI6pPZpcrr1GMKkRLEWht8,4338
|
|
9
9
|
nova/trame/view/components/__init__.py,sha256=60BeS69aOrFnkptjuD17rfPE1f4Z35iBH56TRmW5MW8,451
|
|
10
|
-
nova/trame/view/components/data_selector.py,sha256=
|
|
10
|
+
nova/trame/view/components/data_selector.py,sha256=yQSxlotZX21pEZsKJKFEVQytN5PbjUCBMHPsROa4tjI,19127
|
|
11
11
|
nova/trame/view/components/execution_buttons.py,sha256=Br6uAmE5bY67TTYc5ZTHECNJ_RJqKmv17HAKPpQtbeg,4576
|
|
12
12
|
nova/trame/view/components/file_upload.py,sha256=WOaFXeNNwN0DYZJr-W6vWdBiTpr7m-lq3WKJaHmeMe8,4560
|
|
13
|
-
nova/trame/view/components/input_field.py,sha256=
|
|
13
|
+
nova/trame/view/components/input_field.py,sha256=viP6iP3aKkx-nwC6VYLCQYvV8yEVC817cuWezcp9cpc,17823
|
|
14
14
|
nova/trame/view/components/ornl/__init__.py,sha256=HnxzzSsxw0vQSDCVFfWsAxx1n3HnU37LMuQkfiewmSU,90
|
|
15
|
-
nova/trame/view/components/ornl/neutron_data_selector.py,sha256=
|
|
15
|
+
nova/trame/view/components/ornl/neutron_data_selector.py,sha256=m_XAXdqTelUj6x1kj0kBq2dClVytTQqZ4a6fgNxGloA,17149
|
|
16
16
|
nova/trame/view/components/progress_bar.py,sha256=zhbJwPy_HPQ8YL-ISN8sCRUQ7qY6qqo9wiV59BmvL8I,3038
|
|
17
17
|
nova/trame/view/components/remote_file_input.py,sha256=mcz_bmI2rD8gdmIOKLhlzfj-XoWBwC99T9ZgQORaKqE,14674
|
|
18
18
|
nova/trame/view/components/tool_outputs.py,sha256=IbYV4VjrkWAE354Bh5KH76SPsxGLIkOXChijS4-ce_Y,2408
|
|
@@ -20,28 +20,28 @@ nova/trame/view/components/visualization/__init__.py,sha256=reqkkbhD5uSksHHlhVMy
|
|
|
20
20
|
nova/trame/view/components/visualization/interactive_2d_plot.py,sha256=z2s1janxAclpMEdDJk3z-CQ6r3KPNoR_SXPx9ppWnuQ,3481
|
|
21
21
|
nova/trame/view/components/visualization/matplotlib_figure.py,sha256=8JgbF6elZC8EFZn-c2mLqGSyg0Lb4NLVAKJVSBb9-5g,16010
|
|
22
22
|
nova/trame/view/layouts/__init__.py,sha256=cMrlB5YMUoK8EGB83b34UU0kPTVrH8AxsYvKRtpUNEc,141
|
|
23
|
-
nova/trame/view/layouts/grid.py,sha256=
|
|
24
|
-
nova/trame/view/layouts/hbox.py,sha256=
|
|
23
|
+
nova/trame/view/layouts/grid.py,sha256=3zm9rVa-cexqKCJXZPIGpfrZIbi7qaAGYSmUg7njTS4,6265
|
|
24
|
+
nova/trame/view/layouts/hbox.py,sha256=EOh-GT2rkt5qsvpBLjtrwdHlOA3hopcpFo7_TpxDOVs,4014
|
|
25
25
|
nova/trame/view/layouts/utils.py,sha256=Hg34VQWTG3yHBsgNvmfatR4J-uL3cko7UxSJpT-h3JI,376
|
|
26
|
-
nova/trame/view/layouts/vbox.py,sha256=
|
|
26
|
+
nova/trame/view/layouts/vbox.py,sha256=DrhBjDe2m4V42JV7Ma2YSPkJyaI6k74Yc9U1MgyuFfQ,4018
|
|
27
27
|
nova/trame/view/theme/__init__.py,sha256=70_marDlTigIcPEOGiJb2JTs-8b2sGM5SlY7XBPtBDM,54
|
|
28
|
-
nova/trame/view/theme/assets/core_style.scss,sha256=
|
|
29
|
-
nova/trame/view/theme/assets/favicon.png,sha256=
|
|
28
|
+
nova/trame/view/theme/assets/core_style.scss,sha256=IR3xekkhiJen1HVYOjbb2wB8JG80_7INzDtgosAdkOU,5290
|
|
29
|
+
nova/trame/view/theme/assets/favicon.png,sha256=F5r6SjZet8uARzESJgwRZGK_Q7pDguDG11hyOWIBOc4,11812
|
|
30
30
|
nova/trame/view/theme/assets/js/delay_manager.js,sha256=BN4OL88QsyZG4XQ1sTorHpN1rwD4GnWoVKHvl5F5ydo,776
|
|
31
31
|
nova/trame/view/theme/assets/js/lodash.min.js,sha256=KCyAYJ-fsqtp_HMwbjhy6IKjlA5lrVrtWt1JdMsC57k,73016
|
|
32
|
-
nova/trame/view/theme/assets/js/revo_grid.js,sha256=
|
|
32
|
+
nova/trame/view/theme/assets/js/revo_grid.js,sha256=KSUc8fOoX_BrOVtzIHcLscAiS4qLUclrbo1ndD2cj-A,6128
|
|
33
33
|
nova/trame/view/theme/assets/vuetify_config.json,sha256=a0FSgpLYWGFlRGSMhMq61MyDFBEBwvz55G4qjkM08cs,5627
|
|
34
34
|
nova/trame/view/theme/exit_button.py,sha256=GrgCMLytIrDTWAMtkuFv5JaIBpslFrmI5k2izklSqQs,3872
|
|
35
35
|
nova/trame/view/theme/theme.py,sha256=YecxrUw5I-7NDTyC8jrVdpb49U1TMzIbwO7jDid_jN0,14715
|
|
36
36
|
nova/trame/view/utilities/local_storage.py,sha256=vD8f2VZIpxhIKjZwEaD7siiPCTZO4cw9AfhwdawwYLY,3218
|
|
37
|
-
nova/trame/view_model/data_selector.py,sha256=
|
|
37
|
+
nova/trame/view_model/data_selector.py,sha256=Z_aLHcxatxneyjUBwrus79p_4YQcolmYohNktlraI9k,3507
|
|
38
38
|
nova/trame/view_model/execution_buttons.py,sha256=MfKSp95D92EqpD48C15cBo6dLO0Yld4FeRZMJNxJf7Y,3551
|
|
39
|
-
nova/trame/view_model/ornl/neutron_data_selector.py,sha256=
|
|
39
|
+
nova/trame/view_model/ornl/neutron_data_selector.py,sha256=PcnUUNN3S1i-Tc9G4ZZP6NPGcVhHtA0tx9cwlZ5g_5Y,1848
|
|
40
40
|
nova/trame/view_model/progress_bar.py,sha256=6AUKHF3hfzbdsHqNEnmHRgDcBKY5TT8ywDx9S6ovnsc,2854
|
|
41
41
|
nova/trame/view_model/remote_file_input.py,sha256=zWOflmCDJYYR_pacHphwzricV667GSRokh-mlxpBAOo,3646
|
|
42
42
|
nova/trame/view_model/tool_outputs.py,sha256=ev6LY7fJ0H2xAJn9f5ww28c8Kpom2SYc2FbvFcoN4zg,829
|
|
43
|
-
nova_trame-1.
|
|
44
|
-
nova_trame-1.
|
|
45
|
-
nova_trame-1.
|
|
46
|
-
nova_trame-1.
|
|
47
|
-
nova_trame-1.
|
|
43
|
+
nova_trame-1.1.1.dist-info/METADATA,sha256=WiJNoS62hn4IFYkbLetnV0ivP0lfpM0UFMjthyot-2I,2096
|
|
44
|
+
nova_trame-1.1.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
45
|
+
nova_trame-1.1.1.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
|
|
46
|
+
nova_trame-1.1.1.dist-info/licenses/LICENSE,sha256=Iu5QiDbwNbREg75iYaxIJ_V-zppuv4QFuBhAW-qiAlM,1061
|
|
47
|
+
nova_trame-1.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|