Răsfoiți Sursa

Merge branch 'main' into on-air

Rodja Trappe 1 an în urmă
părinte
comite
8e4a82300b

+ 17 - 1
nicegui/elements/audio.js

@@ -1,9 +1,25 @@
 export default {
-  template: `<audio :controls="controls" :autoplay="autoplay" :muted="muted" :src="src" />`,
+  template: `<audio :controls="controls" :autoplay="autoplay" :muted="muted" :src="computed_src" />`,
   props: {
     controls: Boolean,
     autoplay: Boolean,
     muted: Boolean,
     src: String,
   },
+  data: function () {
+    return {
+      computed_src: undefined,
+    };
+  },
+  mounted() {
+    setTimeout(() => this.compute_src(), 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
+  },
+  updated() {
+    this.compute_src();
+  },
+  methods: {
+    compute_src() {
+      this.computed_src = (this.src.startsWith("/") ? window.path_prefix : "") + this.src;
+    },
+  },
 };

+ 28 - 0
nicegui/elements/image.js

@@ -0,0 +1,28 @@
+export default {
+  template: `
+    <q-img v-bind="$attrs" :src="computed_src">
+      <template v-for="(_, slot) in $slots" v-slot:[slot]="slotProps">
+        <slot :name="slot" v-bind="slotProps || {}" />
+      </template>
+    </q-img>
+  `,
+  props: {
+    src: String,
+  },
+  data: function () {
+    return {
+      computed_src: undefined,
+    };
+  },
+  mounted() {
+    setTimeout(() => this.compute_src(), 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
+  },
+  updated() {
+    this.compute_src();
+  },
+  methods: {
+    compute_src() {
+      this.computed_src = (this.src.startsWith("/") ? window.path_prefix : "") + this.src;
+    },
+  },
+};

+ 5 - 1
nicegui/elements/image.py

@@ -1,8 +1,12 @@
 from pathlib import Path
 from typing import Union
 
+from nicegui.dependencies import register_component
+
 from .mixins.source_element import SourceElement
 
+register_component('image', __file__, 'image.js')
+
 
 class Image(SourceElement):
 
@@ -13,4 +17,4 @@ class Image(SourceElement):
 
         :param source: the source of the image; can be a URL, local file path or a base64 string
         """
-        super().__init__(tag='q-img', source=source)
+        super().__init__(tag='image', source=source)

+ 11 - 1
nicegui/elements/interactive_image.js

@@ -1,7 +1,7 @@
 export default {
   template: `
     <div style="position:relative">
-      <img :src="src" style="width:100%; height:100%;" v-on="onEvents" draggable="false" />
+      <img :src="computed_src" style="width:100%; height:100%;" v-on="onEvents" draggable="false" />
       <svg style="position:absolute;top:0;left:0;pointer-events:none" :viewBox="viewBox">
         <g v-if="cross" :style="{ display: cssDisplay }">
           <line :x1="x" y1="0" :x2="x" y2="100%" stroke="black" />
@@ -18,9 +18,19 @@ export default {
       x: 100,
       y: 100,
       cssDisplay: "none",
+      computed_src: undefined,
     };
   },
+  mounted() {
+    setTimeout(() => this.compute_src(), 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
+  },
+  updated() {
+    this.compute_src();
+  },
   methods: {
+    compute_src() {
+      this.computed_src = (this.src.startsWith("/") ? window.path_prefix : "") + this.src;
+    },
     updateCrossHair(e) {
       this.x = (e.offsetX * e.target.naturalWidth) / e.target.clientWidth;
       this.y = (e.offsetY * e.target.naturalHeight) / e.target.clientHeight;

+ 7 - 3
nicegui/elements/upload.js

@@ -7,11 +7,15 @@ export default {
     </q-uploader>
   `,
   mounted() {
-    setTimeout(() => {
-      this.computed_url = (window.path_prefix || "") + this.url;
-    }, 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
+    setTimeout(() => compute_url, 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
+  },
+  updated() {
+    this.compute_url();
   },
   methods: {
+    compute_url() {
+      this.computed_url = (this.url.startsWith("/") ? window.path_prefix : "") + this.url;
+    },
     reset() {
       this.$refs.uploader.reset();
     },

+ 20 - 6
nicegui/elements/video.js

@@ -1,14 +1,28 @@
 export default {
-  template: `<video :controls="controls" :autoplay="autoplay" :muted="muted" :src="src" />`,
-  methods: {
-    seek(seconds) {
-      this.$el.currentTime = seconds;
-    },
-  },
+  template: `<video :controls="controls" :autoplay="autoplay" :muted="muted" :src="computed_src" />`,
   props: {
     controls: Boolean,
     autoplay: Boolean,
     muted: Boolean,
     src: String,
   },
+  data: function () {
+    return {
+      computed_src: undefined,
+    };
+  },
+  mounted() {
+    setTimeout(() => this.compute_src(), 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
+  },
+  updated() {
+    this.compute_src();
+  },
+  methods: {
+    compute_src() {
+      this.computed_src = (this.src.startsWith("/") ? window.path_prefix : "") + this.src;
+    },
+    seek(seconds) {
+      this.$el.currentTime = seconds;
+    },
+  },
 };

+ 3 - 1
nicegui/run.py

@@ -18,6 +18,8 @@ from . import native as native_module
 from . import native_mode
 from .language import Language
 
+APP_IMPORT_STRING = 'nicegui:app'
+
 
 class Server(uvicorn.Server):
 
@@ -131,7 +133,7 @@ def run(*,
     # NOTE: The following lines are basically a copy of `uvicorn.run`, but keep a reference to the `server`.
 
     config = uvicorn.Config(
-        'nicegui:app' if reload else globals.app,
+        APP_IMPORT_STRING if reload else globals.app,
         host=host,
         port=port,
         reload=reload,

+ 21 - 1
website/static/search_index.json

@@ -366,7 +366,7 @@
   },
   {
     "title": "Button",
-    "content": "This element is based on Quasar's QBtn <https://quasar.dev/vue-components/button>_ component.  The `color parameter accepts a Quasar color, a Tailwind color, or a CSS color. If a Quasar color is used, the button will be styled according to the Quasar theme including the color of the text. Note that there are colors like \"red\" being both a Quasar color and a CSS color. In such cases the Quasar color will be used.  :param text: the label of the button :param on_click: callback which is invoked when button is pressed :param color: the color of the button (either a Quasar, Tailwind, or CSS color or None`, default: 'primary')",
+    "content": "This element is based on Quasar's QBtn <https://quasar.dev/vue-components/button>_ component.  The `color parameter accepts a Quasar color, a Tailwind color, or a CSS color. If a Quasar color is used, the button will be styled according to the Quasar theme including the color of the text. Note that there are colors like \"red\" being both a Quasar color and a CSS color. In such cases the Quasar color will be used.  :param text: the label of the button :param on_click: callback which is invoked when button is pressed :param color: the color of the button (either a Quasar, Tailwind, or CSS color or None, default: 'primary') :param icon: the name of an icon to be displayed on the button (default: None`)",
     "url": "/documentation/button"
   },
   {
@@ -559,6 +559,11 @@
     "content": "Adds global keyboard event tracking.  :param on_key: callback to be executed when keyboard events occur. :param active: boolean flag indicating whether the callback should be executed or not (default: True) :param repeating: boolean flag indicating whether held keys should be sent repeatedly (default: True) :param ignore: ignore keys when one of these element types is focussed (default: ['input', 'select', 'button', 'textarea'])",
     "url": "/documentation/keyboard"
   },
+  {
+    "title": "Stepper",
+    "content": "This element represents Quasar's QStepper <https://quasar.dev/vue-components/stepper#qstepper-api>_ component. It contains individual steps.  :param value: ui.step or name of the step to be initially selected (default: None meaning the first step) :param on_value_change: callback to be executed when the selected step changes",
+    "url": "/documentation/stepper"
+  },
   {
     "title": "Row Element",
     "content": "Provides a container which arranges its child in a row.",
@@ -689,6 +694,11 @@
     "content": "The @ui.refreshable decorator allows you to create functions that have a refresh method. This method will automatically delete all elements created by the function and recreate them.",
     "url": "/documentation/refreshable"
   },
+  {
+    "title": "Refreshable: Refreshable UI for input validation",
+    "content": "Here is a demo of how to use the refreshable decorator to give feedback about the validity of user input.",
+    "url": "/documentation/refreshable#refreshable_ui_for_input_validation"
+  },
   {
     "title": "Color Input",
     "content": ":param label: displayed label for the color input :param placeholder: text to show if no color is selected :param value: the current color value :param on_change: callback to execute when the value changes",
@@ -779,6 +789,16 @@
     "content": "Displays an image.  :param source: the source of the image; can be a URL, local file path or a base64 string",
     "url": "/documentation/image"
   },
+  {
+    "title": "Image: Local files",
+    "content": "You can use local images as well by passing a path to the image file.",
+    "url": "/documentation/image#local_files"
+  },
+  {
+    "title": "Image: Base64 string",
+    "content": "You can also use a Base64 string as image source.",
+    "url": "/documentation/image#base64_string"
+  },
   {
     "title": "Image: Lottie files",
     "content": "You can also use Lottie files with animations.",