nova-trame 0.11.1__py3-none-any.whl → 0.12.0__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.
- nova/trame/model/remote_file_input.py +20 -9
- nova/trame/view/components/remote_file_input.py +15 -12
- nova/trame/view_model/remote_file_input.py +17 -8
- {nova_trame-0.11.1.dist-info → nova_trame-0.12.0.dist-info}/METADATA +1 -1
- {nova_trame-0.11.1.dist-info → nova_trame-0.12.0.dist-info}/RECORD +8 -8
- {nova_trame-0.11.1.dist-info → nova_trame-0.12.0.dist-info}/LICENSE +0 -0
- {nova_trame-0.11.1.dist-info → nova_trame-0.12.0.dist-info}/WHEEL +0 -0
- {nova_trame-0.11.1.dist-info → nova_trame-0.12.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,8 @@
|
|
1
1
|
"""Model state for RemoteFileInput."""
|
2
2
|
|
3
3
|
import os
|
4
|
+
from functools import cmp_to_key
|
5
|
+
from locale import strcoll
|
4
6
|
from typing import Any, Union
|
5
7
|
|
6
8
|
|
@@ -17,8 +19,11 @@ class RemoteFileInputModel:
|
|
17
19
|
def get_base_paths(self) -> list[dict[str, Any]]:
|
18
20
|
return [{"path": base_path, "directory": True} for base_path in self.base_paths]
|
19
21
|
|
20
|
-
def scan_current_path(
|
22
|
+
def scan_current_path(
|
23
|
+
self, current_path: str, showing_all_files: bool, filter: str
|
24
|
+
) -> tuple[list[dict[str, Any]], bool]:
|
21
25
|
failed = False
|
26
|
+
filter = filter.split("/")[-1]
|
22
27
|
|
23
28
|
try:
|
24
29
|
if current_path and (not self.valid_subpath(current_path) or not os.path.exists(current_path)):
|
@@ -32,13 +37,24 @@ class RemoteFileInputModel:
|
|
32
37
|
scan_path = os.path.dirname(current_path)
|
33
38
|
|
34
39
|
for entry in os.scandir(scan_path):
|
35
|
-
if self.valid_entry(entry, showing_all_files):
|
40
|
+
if self.valid_entry(entry, showing_all_files) and (not filter or entry.name.startswith(filter)):
|
36
41
|
files.append({"path": entry.name, "directory": entry.is_dir()})
|
37
42
|
except OSError:
|
38
43
|
files = self.get_base_paths()
|
39
44
|
failed = True
|
40
45
|
|
41
|
-
|
46
|
+
def _sort_files(a: dict[str, Any], b: dict[str, Any]) -> int:
|
47
|
+
if a["directory"] and not b["directory"]:
|
48
|
+
return -1
|
49
|
+
if b["directory"] and not a["directory"]:
|
50
|
+
return 1
|
51
|
+
|
52
|
+
path_a = a["path"].lower()
|
53
|
+
path_b = b["path"].lower()
|
54
|
+
|
55
|
+
return strcoll(path_a, path_b)
|
56
|
+
|
57
|
+
sorted_files = sorted(files, key=cmp_to_key(_sort_files))
|
42
58
|
|
43
59
|
return (sorted_files, failed)
|
44
60
|
|
@@ -86,13 +102,8 @@ class RemoteFileInputModel:
|
|
86
102
|
if subpath == "":
|
87
103
|
return False
|
88
104
|
|
89
|
-
try:
|
90
|
-
real_path = os.path.realpath(subpath)
|
91
|
-
except TypeError:
|
92
|
-
return False
|
93
|
-
|
94
105
|
for base_path in self.base_paths:
|
95
|
-
if
|
106
|
+
if subpath.startswith(base_path):
|
96
107
|
return True
|
97
108
|
|
98
109
|
return False
|
@@ -1,7 +1,7 @@
|
|
1
1
|
"""View implementation for RemoteFileInput."""
|
2
2
|
|
3
3
|
from functools import partial
|
4
|
-
from typing import Any, Optional, cast
|
4
|
+
from typing import Any, Optional, Union, cast
|
5
5
|
|
6
6
|
from trame.app import get_server
|
7
7
|
from trame.widgets import client, html
|
@@ -22,7 +22,7 @@ class RemoteFileInput:
|
|
22
22
|
|
23
23
|
def __init__(
|
24
24
|
self,
|
25
|
-
v_model: Optional[str] = None,
|
25
|
+
v_model: Optional[Union[tuple[str, Any], str]] = None,
|
26
26
|
allow_files: bool = True,
|
27
27
|
allow_folders: bool = False,
|
28
28
|
allow_nonexistent_path: bool = False,
|
@@ -35,8 +35,9 @@ class RemoteFileInput:
|
|
35
35
|
|
36
36
|
Parameters
|
37
37
|
----------
|
38
|
-
v_model : str
|
39
|
-
The v-model for
|
38
|
+
v_model : tuple[str, Any] or str, optional
|
39
|
+
The v-model for this component. If this references a Pydantic configuration variable, then this component
|
40
|
+
will attempt to load a label, hint, and validation rules from the configuration for you automatically.
|
40
41
|
allow_files : bool
|
41
42
|
If true, the user can save a file selection.
|
42
43
|
allow_folders : bool
|
@@ -100,19 +101,17 @@ class RemoteFileInput:
|
|
100
101
|
vuetify.VIcon("mdi-folder-open")
|
101
102
|
|
102
103
|
with vuetify.VDialog(
|
103
|
-
v_model=self.vm.get_dialog_state_name(),
|
104
|
-
activator="parent",
|
105
|
-
persistent=True,
|
106
|
-
**self.dialog_props,
|
104
|
+
v_model=self.vm.get_dialog_state_name(), activator="parent", **self.dialog_props
|
107
105
|
):
|
108
106
|
with vuetify.VCard(classes="pa-4"):
|
109
107
|
vuetify.VCardTitle(input.label)
|
110
108
|
vuetify.VTextField(
|
111
|
-
v_model=self.
|
109
|
+
v_model=self.vm.get_filter_state_name(),
|
112
110
|
classes="mb-4 px-4",
|
113
111
|
label="Current Selection",
|
114
112
|
__events=["change"],
|
115
113
|
change=(self.vm.select_file, "[$event.target.value]"),
|
114
|
+
update_modelValue=(self.vm.filter_paths, "[$event]"),
|
116
115
|
)
|
117
116
|
|
118
117
|
if self.allow_files and self.extensions:
|
@@ -174,15 +173,19 @@ class RemoteFileInput:
|
|
174
173
|
server = get_server(None, client_type="vue3")
|
175
174
|
binding = TrameBinding(server.state)
|
176
175
|
|
176
|
+
if isinstance(self.v_model, tuple):
|
177
|
+
model_name = self.v_model[0]
|
178
|
+
else:
|
179
|
+
model_name = self.v_model
|
180
|
+
|
177
181
|
self.vm = RemoteFileInputViewModel(self.model, binding)
|
178
182
|
|
179
183
|
self.vm.dialog_bind.connect(self.vm.get_dialog_state_name())
|
180
184
|
self.vm.file_list_bind.connect(self.vm.get_file_list_state_name())
|
185
|
+
self.vm.filter_bind.connect(self.vm.get_filter_state_name())
|
181
186
|
self.vm.on_close_bind.connect(client.JSEval(exec=f"{self.vm.get_dialog_state_name()} = false;").exec)
|
182
187
|
self.vm.on_update_bind.connect(
|
183
|
-
client.JSEval(
|
184
|
-
exec=f"{self.v_model} = $event; flushState('{self.v_model.split('.')[0].split('[')[0]}');"
|
185
|
-
).exec
|
188
|
+
client.JSEval(exec=f"{model_name} = $event; flushState('{model_name.split('.')[0].split('[')[0]}');").exec
|
186
189
|
)
|
187
190
|
self.vm.showing_all_bind.connect(self.vm.get_showing_all_state_name())
|
188
191
|
self.vm.valid_selection_bind.connect(self.vm.get_valid_selection_state_name())
|
@@ -19,12 +19,13 @@ class RemoteFileInputViewModel:
|
|
19
19
|
self.id = RemoteFileInputViewModel.counter
|
20
20
|
RemoteFileInputViewModel.counter += 1
|
21
21
|
|
22
|
-
self.previous_value = ""
|
23
22
|
self.showing_all_files = False
|
24
23
|
self.showing_base_paths = True
|
24
|
+
self.previous_value = ""
|
25
25
|
self.value = ""
|
26
26
|
self.dialog_bind = binding.new_bind()
|
27
27
|
self.file_list_bind = binding.new_bind()
|
28
|
+
self.filter_bind = binding.new_bind()
|
28
29
|
self.showing_all_bind = binding.new_bind()
|
29
30
|
self.valid_selection_bind = binding.new_bind()
|
30
31
|
self.on_close_bind = binding.new_bind()
|
@@ -35,18 +36,26 @@ class RemoteFileInputViewModel:
|
|
35
36
|
self.populate_file_list()
|
36
37
|
|
37
38
|
def close_dialog(self, cancel: bool = False) -> None:
|
38
|
-
if cancel:
|
39
|
-
self.value = self.previous_value
|
39
|
+
if not cancel:
|
40
40
|
self.on_update_bind.update_in_view(self.value)
|
41
|
+
else:
|
42
|
+
self.value = self.previous_value
|
41
43
|
|
44
|
+
self.filter_bind.update_in_view(self.value)
|
42
45
|
self.on_close_bind.update_in_view(None)
|
43
46
|
|
47
|
+
def filter_paths(self, filter: str) -> None:
|
48
|
+
self.populate_file_list(filter)
|
49
|
+
|
44
50
|
def get_dialog_state_name(self) -> str:
|
45
51
|
return f"nova__dialog_{self.id}"
|
46
52
|
|
47
53
|
def get_file_list_state_name(self) -> str:
|
48
54
|
return f"nova__file_list_{self.id}"
|
49
55
|
|
56
|
+
def get_filter_state_name(self) -> str:
|
57
|
+
return f"nova__filter_{self.id}"
|
58
|
+
|
50
59
|
def get_showing_all_state_name(self) -> str:
|
51
60
|
return f"nova__showing_all_{self.id}"
|
52
61
|
|
@@ -66,19 +75,19 @@ class RemoteFileInputViewModel:
|
|
66
75
|
self.showing_all_bind.update_in_view(self.showing_all_files)
|
67
76
|
self.populate_file_list()
|
68
77
|
|
69
|
-
def populate_file_list(self) -> None:
|
70
|
-
files = self.scan_current_path()
|
78
|
+
def populate_file_list(self, filter: str = "") -> None:
|
79
|
+
files = self.scan_current_path(filter)
|
71
80
|
self.file_list_bind.update_in_view(files)
|
72
81
|
|
73
|
-
def scan_current_path(self) -> list[dict[str, Any]]:
|
74
|
-
files, self.showing_base_paths = self.model.scan_current_path(self.value, self.showing_all_files)
|
82
|
+
def scan_current_path(self, filter: str) -> list[dict[str, Any]]:
|
83
|
+
files, self.showing_base_paths = self.model.scan_current_path(self.value, self.showing_all_files, filter)
|
75
84
|
|
76
85
|
return files
|
77
86
|
|
78
87
|
def select_file(self, file: Union[dict[str, str], str]) -> None:
|
79
88
|
new_path = self.model.select_file(file, self.value, self.showing_base_paths)
|
80
89
|
self.set_value(new_path)
|
81
|
-
self.
|
90
|
+
self.filter_bind.update_in_view(self.value)
|
82
91
|
|
83
92
|
self.valid_selection_bind.update_in_view(self.model.valid_selection(new_path))
|
84
93
|
self.populate_file_list()
|
@@ -1,9 +1,9 @@
|
|
1
1
|
nova/__init__.py,sha256=ED6jHcYiuYpr_0vjGz0zx2lrrmJT9sDJCzIljoDfmlM,65
|
2
2
|
nova/trame/__init__.py,sha256=gFrAg1qva5PIqR5TjvPzAxLx103IKipJLqp3XXvrQL8,59
|
3
|
-
nova/trame/model/remote_file_input.py,sha256=
|
3
|
+
nova/trame/model/remote_file_input.py,sha256=9KAf31ZHzpsh_aXUrNcF81Q5jvUZDWCzW1QATKls-Jk,3675
|
4
4
|
nova/trame/view/components/__init__.py,sha256=fopr6mVqcpDcVYK9ue7SLUHyswgvRPcFESTq86mu1R8,128
|
5
5
|
nova/trame/view/components/input_field.py,sha256=CvVbuMuSu-vgRgyZsooscGw2nbxBbyangvrx2GmzuS8,12243
|
6
|
-
nova/trame/view/components/remote_file_input.py,sha256=
|
6
|
+
nova/trame/view/components/remote_file_input.py,sha256=k2yrwkell_g0sGnWR9XLL1LxkmFLr8-AGhduo8E-N4A,8669
|
7
7
|
nova/trame/view/components/visualization/__init__.py,sha256=kDX1fkbtAgXSGlqhlMNhYYoYrq-hfS636smjgLsh6gg,84
|
8
8
|
nova/trame/view/components/visualization/interactive_2d_plot.py,sha256=foZCMoqbuahT5dtqIQvm8C4ZJcY9P211eJEcpQJltmM,3421
|
9
9
|
nova/trame/view/layouts/__init__.py,sha256=cMrlB5YMUoK8EGB83b34UU0kPTVrH8AxsYvKRtpUNEc,141
|
@@ -16,9 +16,9 @@ nova/trame/view/theme/assets/favicon.png,sha256=Xbp1nUmhcBDeObjsebEbEAraPDZ_M163
|
|
16
16
|
nova/trame/view/theme/assets/vuetify_config.json,sha256=1EwlDUHZiM45MWgnHSQKgLH_d0IZ1iaXjl3eXxGc2EI,4832
|
17
17
|
nova/trame/view/theme/theme.py,sha256=lHeXZPGiS1Ezn3aiKpoEyarLgxOyatWOzYSApX9VI3M,11402
|
18
18
|
nova/trame/view/utilities/local_storage.py,sha256=vD8f2VZIpxhIKjZwEaD7siiPCTZO4cw9AfhwdawwYLY,3218
|
19
|
-
nova/trame/view_model/remote_file_input.py,sha256=
|
20
|
-
nova_trame-0.
|
21
|
-
nova_trame-0.
|
22
|
-
nova_trame-0.
|
23
|
-
nova_trame-0.
|
24
|
-
nova_trame-0.
|
19
|
+
nova/trame/view_model/remote_file_input.py,sha256=WHWCQkZBGeKLe1aTPbtVNI8tn-PDt64mi1-561uuBpQ,3320
|
20
|
+
nova_trame-0.12.0.dist-info/LICENSE,sha256=MOqZ8tPMKy8ZETJ2-HEvFTZ7dYNlg3gXmBkV-Y9i8bw,1061
|
21
|
+
nova_trame-0.12.0.dist-info/METADATA,sha256=q7FycKbwTX_JY6zo5fa9Ep59ZCcv1zUr__HsGSaiwNg,1240
|
22
|
+
nova_trame-0.12.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
23
|
+
nova_trame-0.12.0.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
|
24
|
+
nova_trame-0.12.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|