diff --git a/ipyvuetify/extra/file_input.py b/ipyvuetify/extra/file_input.py index 30fb52b5..f76a41de 100644 --- a/ipyvuetify/extra/file_input.py +++ b/ipyvuetify/extra/file_input.py @@ -3,10 +3,13 @@ import io import os import sys +import warnings import IPython import nest_asyncio import traitlets +from ipywidgets import dlink, widget_serialization +from traitlets import Any, Bool, Dict, Float, Int, List, Unicode, Union import ipyvuetify as v @@ -52,7 +55,7 @@ def __init__(self, widget, file_index, timeout=30): self.timeout = timeout self.valid = True self.offset = 0 - self.size = widget.file_info[file_index]["size"] + self.size = widget.v_model[file_index]["size"] self.chunk_queue = [] @@ -148,26 +151,178 @@ def readall(self): class FileInput(v.VuetifyTemplate): - template = traitlets.Unicode(load_template("file_input.vue")).tag(sync=True) - data = traitlets.Unicode("{myfiles: undefined}").tag(sync=True) - - file_info = traitlets.List().tag(sync=True) - version = traitlets.Int(0).tag(sync=True) - multiple = traitlets.Bool(True).tag(sync=True) - disabled = traitlets.Bool(False).tag(sync=True) - directory = traitlets.Bool(False).tag(sync=True) - accept = traitlets.Unicode().tag(sync=True) - total_progress = traitlets.Int(0).tag(sync=True) - show_progress = traitlets.Bool(True).tag(sync=True) - progress_indeterminate = traitlets.Bool(False).tag(sync=True) + append_icon = Unicode(None, allow_none=True).tag(sync=True) + + append_outer_icon = Unicode(None, allow_none=True).tag(sync=True) + + autofocus = Bool(None, allow_none=True).tag(sync=True) + + background_color = Unicode(None, allow_none=True).tag(sync=True) + + chips = Bool(None, allow_none=True).tag(sync=True) + + clear_icon = Unicode(None, allow_none=True).tag(sync=True) + + clearable = Bool(None, allow_none=True).tag(sync=True) + + color = Unicode(None, allow_none=True).tag(sync=True) + + counter = Union([Bool(), Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + counter_size_string = Unicode(None, allow_none=True).tag(sync=True) + + counter_string = Unicode(None, allow_none=True).tag(sync=True) + + dark = Bool(None, allow_none=True).tag(sync=True) + + dense = Bool(None, allow_none=True).tag(sync=True) + + disabled = Bool(None, allow_none=True).tag(sync=True) + + error = Bool(None, allow_none=True).tag(sync=True) + + error_count = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + error_messages = Union([Unicode(), List(Any())], default_value=None, allow_none=True).tag(sync=True) + + filled = Bool(None, allow_none=True).tag(sync=True) + + flat = Bool(None, allow_none=True).tag(sync=True) + + full_width = Bool(None, allow_none=True).tag(sync=True) + + height = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + hide_details = Union([Bool(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + # Missing Vuetify prop: hide-input + + hint = Unicode(None, allow_none=True).tag(sync=True) + + id = Unicode(None, allow_none=True).tag(sync=True) + + label = Unicode(None, allow_none=True).tag(sync=True) + + light = Bool(None, allow_none=True).tag(sync=True) + + loader_height = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + loading = Union([Bool(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + messages = Union([Unicode(), List(Any())], default_value=None, allow_none=True).tag(sync=True) + + multiple = Bool(None, allow_none=True).tag(sync=True) + + outlined = Bool(None, allow_none=True).tag(sync=True) + + persistent_hint = Bool(None, allow_none=True).tag(sync=True) + + # Missing Vuetify prop: persistent-placeholder + + placeholder = Unicode(None, allow_none=True).tag(sync=True) + + prefix = Unicode(None, allow_none=True).tag(sync=True) + + prepend_icon = Unicode(None, allow_none=True).tag(sync=True) + + prepend_inner_icon = Unicode(None, allow_none=True).tag(sync=True) + + readonly = Bool(None, allow_none=True).tag(sync=True) + + reverse = Bool(None, allow_none=True).tag(sync=True) + + rounded = Bool(None, allow_none=True).tag(sync=True) + + rules = List(Any(), default_value=None, allow_none=True).tag(sync=True) + + shaped = Bool(None, allow_none=True).tag(sync=True) + + show_size = Union([Bool(), Float()], default_value=None, allow_none=True).tag(sync=True) + + single_line = Bool(None, allow_none=True).tag(sync=True) + + small_chips = Bool(None, allow_none=True).tag(sync=True) + + solo = Bool(None, allow_none=True).tag(sync=True) + + solo_inverted = Bool(None, allow_none=True).tag(sync=True) + + success = Bool(None, allow_none=True).tag(sync=True) + + success_messages = Union([Unicode(), List(Any())], default_value=None, allow_none=True).tag(sync=True) + + suffix = Unicode(None, allow_none=True).tag(sync=True) + + truncate_length = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True) + + type = Unicode(None, allow_none=True).tag(sync=True) + + validate_on_blur = Bool(None, allow_none=True).tag(sync=True) + + value = Any(None, allow_none=True).tag(sync=True) + + # VueWidget props + + v_model = List(Dict(), allow_none=True).tag(sync=True) + + style_ = Unicode(None, allow_none=True).tag(sync=True) + + class_ = Unicode(None, allow_none=True).tag(sync=True) + + attributes = Dict(None, allow_none=True).tag(sync=True) + + v_slots = List(Dict()).tag(sync=True, **widget_serialization) + + v_on = Unicode(None, allow_none=True).tag(sync=True) + + # Component-specific props + + template = Unicode(load_template("file_input.vue")).tag(sync=True) + + version = Int(0).tag(sync=True) + + total_progress = Int(0).tag(sync=True) + + progress_indeterminate = Bool(False).tag(sync=True) + + file_info = List(Dict(), allow_none=True) # Deprecated, use v_model instead total_progress_inner = 0 total_size_inner = 0 - def __init__(self, **kwargs): + def __init__(self, *args, **kwargs): self.chunk_listeners = {} self.stats = [] - super().__init__(**kwargs) + + # Keep default behaviour + kwargs.setdefault("multiple", True) + kwargs.setdefault("clearable", True) + + attributes = kwargs.setdefault("attributes", {}) + if "accept" in kwargs: + warnings.warn("accept argument is deprecated, use attributes dictionary instead", DeprecationWarning) + attributes["accept"] = kwargs["accept"] + del kwargs["accept"] + if "directory" in kwargs: + warnings.warn("directory argument is deprecated, use attributes dictionary instead", DeprecationWarning) + attributes["directory"] = kwargs["directory"] + del kwargs["directory"] + if "show_progress" in kwargs: + warnings.warn( + "show_progress argument is deprecated, use v_slots to change the progress slot", DeprecationWarning + ) + del kwargs["show_progress"] + if "file_info" in kwargs: + warnings.warn("file_info cannot be set on FileInput") + del kwargs["file_info"] + if "v_model" in kwargs: + warnings.warn("v_model cannot be set on FileInput") + del kwargs["v_model"] + + # Maintain backwards compatibility for the file_info + dlink((self, "v_model"), (self, "file_info")) + + super().__init__(*args, **kwargs) if not hasattr(IPython.get_ipython(), "kernel"): return @@ -175,8 +330,26 @@ def __init__(self, **kwargs): if kernel.implementation == "ipython": nest_asyncio.apply() - @traitlets.observe("file_info") - def _file_info_changed(self, _): + # @property + # def file_info(self): + # warnings.warn("file_info is deprecated, use v_model instead", DeprecationWarning) + # return self.v_model + + # @file_info.setter + # def file_info(self, _): + # warnings.warn("file_info is deprecated and cannot be set on FileInput") + + @property + def show_progress(self): + warnings.warn("show_progress is deprecated, use v_slots to change the progress slot", DeprecationWarning) + return True + + @show_progress.setter + def show_progress(self, _): + warnings.warn("show_progress is deprecated, use v_slots to change the progress slot", DeprecationWarning) + + @traitlets.observe("v_model") + def _v_model_changed(self, _): self.version += 1 self.reset_stats() @@ -190,8 +363,8 @@ def update_stats(self, file_index, bytes_read): def get_files(self, timeout=30): files = [] - for index, file in enumerate(self.file_info): - file = copy.deepcopy(self.file_info[index]) + for index, file in enumerate(self.v_model): + file = copy.deepcopy(self.v_model[index]) file["file_obj"] = ClientSideFile(self, index, timeout=timeout) files.append(file) return files @@ -201,10 +374,10 @@ def clear(self): self.send({"method": "clear", "args": []}) def reset_stats(self): - self.stats = [0 for _ in self.file_info] + self.stats = [0 for _ in self.v_model] self.total_progress = 0 self.total_progress_inner = 0 - self.total_size_inner = sum([f["size"] for f in self.file_info]) + self.total_size_inner = sum([f["size"] for f in self.v_model]) def vue_upload(self, content, buffers): listener_id = content["id"] diff --git a/ipyvuetify/extra/file_input.vue b/ipyvuetify/extra/file_input.vue index 6d8a8401..3648f8f5 100644 --- a/ipyvuetify/extra/file_input.vue +++ b/ipyvuetify/extra/file_input.vue @@ -1,25 +1,22 @@