nova-trame 0.11.1__py3-none-any.whl → 0.12.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.
@@ -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(self, current_path: str, showing_all_files: bool) -> tuple[list[dict[str, Any]], bool]:
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
- sorted_files = sorted(files, key=lambda entry: str(entry["path"]).lower())
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 real_path.startswith(base_path):
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 the input field.
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.v_model,
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())
@@ -128,16 +128,14 @@
128
128
  "defaults": {
129
129
  "VCard": {
130
130
  "class": "pa-4",
131
- "VListItem": {
132
- "class": "border-primary border-sm",
131
+ "VCard": {
132
+ "class": "border-primary border-sm pa-4",
133
133
  "style": {
134
134
  "background-color": "rgba(var(--v-theme-primary), 0.1)"
135
135
  }
136
- }
137
- },
138
- "VCol": {
139
- "VCard": {
140
- "class": "border-primary border-sm pa-4",
136
+ },
137
+ "VListItem": {
138
+ "class": "border-primary border-sm",
141
139
  "style": {
142
140
  "background-color": "rgba(var(--v-theme-primary), 0.1)"
143
141
  }
@@ -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.on_update_bind.update_in_view(self.value)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nova-trame
3
- Version: 0.11.1
3
+ Version: 0.12.1
4
4
  Summary: A Python Package for injecting curated themes and custom components into Trame applications
5
5
  License: MIT
6
6
  Keywords: NDIP,Python,Trame,Vuetify
@@ -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=GRvWepYcFPjyrMOCI-mgr8GvDSYNag2oGqG-QY1Jfbw,3280
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=FaS1TxFBdNp8xB6CicC5iAX4kSdwePwRCiyTas0d_Tc,8233
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
@@ -13,12 +13,12 @@ nova/trame/view/layouts/vbox.py,sha256=Q4EvrtGJORyNF6AnCLGXToy8XU6yofiO5_kt7hK-A
13
13
  nova/trame/view/theme/__init__.py,sha256=70_marDlTigIcPEOGiJb2JTs-8b2sGM5SlY7XBPtBDM,54
14
14
  nova/trame/view/theme/assets/core_style.scss,sha256=AktysiiCYLeiTzCTtYwkksiUVmqb4S23RlDcW8L1ebI,518
15
15
  nova/trame/view/theme/assets/favicon.png,sha256=Xbp1nUmhcBDeObjsebEbEAraPDZ_M163M_ZLtm5AbQc,1927
16
- nova/trame/view/theme/assets/vuetify_config.json,sha256=1EwlDUHZiM45MWgnHSQKgLH_d0IZ1iaXjl3eXxGc2EI,4832
16
+ nova/trame/view/theme/assets/vuetify_config.json,sha256=7WGV6rO7hv2sapGsX9yy1d-dINshYFXRNX99D9I3dKQ,4780
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=EU8jrU_3_BQqvta5-owSotHG0LAxLbTp6INqk3TdIZc,2986
20
- nova_trame-0.11.1.dist-info/LICENSE,sha256=MOqZ8tPMKy8ZETJ2-HEvFTZ7dYNlg3gXmBkV-Y9i8bw,1061
21
- nova_trame-0.11.1.dist-info/METADATA,sha256=d5nFsxphTE0zomScgQHrmgLRwFOshOtyxbfGuRfBpcM,1240
22
- nova_trame-0.11.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
23
- nova_trame-0.11.1.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
24
- nova_trame-0.11.1.dist-info/RECORD,,
19
+ nova/trame/view_model/remote_file_input.py,sha256=WHWCQkZBGeKLe1aTPbtVNI8tn-PDt64mi1-561uuBpQ,3320
20
+ nova_trame-0.12.1.dist-info/LICENSE,sha256=MOqZ8tPMKy8ZETJ2-HEvFTZ7dYNlg3gXmBkV-Y9i8bw,1061
21
+ nova_trame-0.12.1.dist-info/METADATA,sha256=lNZj7ZRu0tvXmwUnK4vRwKpBCDI4I9qzAZpFcaqM5Tw,1240
22
+ nova_trame-0.12.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
23
+ nova_trame-0.12.1.dist-info/entry_points.txt,sha256=J2AmeSwiTYZ4ZqHHp9HO6v4MaYQTTBPbNh6WtoqOT58,42
24
+ nova_trame-0.12.1.dist-info/RECORD,,