From e974cf045c82ce8d79efdda36b9dbf6ea557baa4 Mon Sep 17 00:00:00 2001 From: Dawood Khan Date: Wed, 20 Dec 2023 21:16:21 -0500 Subject: [PATCH] Custom JS Guide (#6839) * guide * fix * Update guides/03_building-with-blocks/04_custom-CSS-and-JS.md Co-authored-by: Abubakar Abid * Update guides/03_building-with-blocks/04_custom-CSS-and-JS.md Co-authored-by: Abubakar Abid * Update guides/03_building-with-blocks/04_custom-CSS-and-JS.md Co-authored-by: Abubakar Abid * guide fix * add changeset --------- Co-authored-by: Abubakar Abid Co-authored-by: gradio-pr-bot --- .changeset/lemon-waves-smash.md | 6 +++ demo/blocks_js_load/run.ipynb | 1 + demo/blocks_js_load/run.py | 45 +++++++++++++++++++ .../04_custom-CSS-and-JS.md | 45 ++++++++++++++++--- js/app/src/Index.svelte | 8 ++-- 5 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 .changeset/lemon-waves-smash.md create mode 100644 demo/blocks_js_load/run.ipynb create mode 100644 demo/blocks_js_load/run.py diff --git a/.changeset/lemon-waves-smash.md b/.changeset/lemon-waves-smash.md new file mode 100644 index 0000000000..0ca63f8ef1 --- /dev/null +++ b/.changeset/lemon-waves-smash.md @@ -0,0 +1,6 @@ +--- +"@gradio/app": minor +"gradio": minor +--- + +feat:Custom JS Guide diff --git a/demo/blocks_js_load/run.ipynb b/demo/blocks_js_load/run.ipynb new file mode 100644 index 0000000000..c4ec6babf6 --- /dev/null +++ b/demo/blocks_js_load/run.ipynb @@ -0,0 +1 @@ +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: blocks_js_load"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "def welcome(name):\n", " return f\"Welcome to Gradio, {name}!\"\n", "\n", "js = \"\"\"\n", "function createGradioAnimation() {\n", " var container = document.createElement('div');\n", " container.id = 'gradio-animation';\n", " container.style.fontSize = '2em';\n", " container.style.fontWeight = 'bold';\n", " container.style.textAlign = 'center';\n", " container.style.marginBottom = '20px';\n", "\n", " var text = 'Welcome to Gradio!';\n", " for (var i = 0; i < text.length; i++) {\n", " (function(i){\n", " setTimeout(function(){\n", " var letter = document.createElement('span');\n", " letter.style.opacity = '0';\n", " letter.style.transition = 'opacity 0.5s';\n", " letter.innerText = text[i];\n", "\n", " container.appendChild(letter);\n", "\n", " setTimeout(function() {\n", " letter.style.opacity = '1';\n", " }, 50);\n", " }, i * 250);\n", " })(i);\n", " }\n", "\n", " var gradioContainer = document.querySelector('.gradio-container');\n", " gradioContainer.insertBefore(container, gradioContainer.firstChild);\n", "\n", " return 'Animation created';\n", "}\n", "\"\"\"\n", "with gr.Blocks(js=js) as demo:\n", " inp = gr.Textbox(placeholder=\"What is your name?\")\n", " out = gr.Textbox()\n", " inp.change(welcome, inp, out)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/blocks_js_load/run.py b/demo/blocks_js_load/run.py new file mode 100644 index 0000000000..a1c0335e9b --- /dev/null +++ b/demo/blocks_js_load/run.py @@ -0,0 +1,45 @@ +import gradio as gr + +def welcome(name): + return f"Welcome to Gradio, {name}!" + +js = """ +function createGradioAnimation() { + var container = document.createElement('div'); + container.id = 'gradio-animation'; + container.style.fontSize = '2em'; + container.style.fontWeight = 'bold'; + container.style.textAlign = 'center'; + container.style.marginBottom = '20px'; + + var text = 'Welcome to Gradio!'; + for (var i = 0; i < text.length; i++) { + (function(i){ + setTimeout(function(){ + var letter = document.createElement('span'); + letter.style.opacity = '0'; + letter.style.transition = 'opacity 0.5s'; + letter.innerText = text[i]; + + container.appendChild(letter); + + setTimeout(function() { + letter.style.opacity = '1'; + }, 50); + }, i * 250); + })(i); + } + + var gradioContainer = document.querySelector('.gradio-container'); + gradioContainer.insertBefore(container, gradioContainer.firstChild); + + return 'Animation created'; +} +""" +with gr.Blocks(js=js) as demo: + inp = gr.Textbox(placeholder="What is your name?") + out = gr.Textbox() + inp.change(welcome, inp, out) + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/guides/03_building-with-blocks/04_custom-CSS-and-JS.md b/guides/03_building-with-blocks/04_custom-CSS-and-JS.md index c2086579c6..88eba4a27b 100644 --- a/guides/03_building-with-blocks/04_custom-CSS-and-JS.md +++ b/guides/03_building-with-blocks/04_custom-CSS-and-JS.md @@ -1,10 +1,8 @@ -# Custom JS and CSS +# Customizing your demo with CSS and Javascript -This guide covers how to style Blocks with more flexibility, as well as adding Javascript code to event listeners. +Gradio allows you to customize your demo in several ways. You can customize the layout of your demo, add custom HTML, and add custom theming as well. This tutorial will go beyond that and walk you through how to add custom CSS and JavaScript code to your demo in order to add custom styling, animations, custom UI functionality, analytics, and more. -**Warning**: The use of query selectors in custom JS and CSS is _not_ guaranteed to work across Gradio versions as the Gradio HTML DOM may change. We recommend using query selectors sparingly. - -## Custom CSS +## Adding custom CSS to your demo Gradio themes are the easiest way to customize the look and feel of your app. You can choose from a variety of themes, or create your own. To do so, pass the `theme=` kwarg to the `Blocks` constructor. For example: @@ -17,6 +15,8 @@ Gradio comes with a set of prebuilt themes which you can load from `gr.themes.*` For additional styling ability, you can pass any CSS to your app using the `css=` kwarg. You can either the filepath to a CSS file, or a string of CSS code. +**Warning**: The use of query selectors in custom JS and CSS is _not_ guaranteed to work across Gradio versions as the Gradio HTML DOM may change. We recommend using query selectors sparingly. + The base class for the Gradio app is `gradio-container`, so here's an example that changes the background color of the Gradio app: ```python @@ -51,9 +51,40 @@ with gr.Blocks(css=css) as demo: The CSS `#warning` ruleset will only target the second Textbox, while the `.feedback` ruleset will target both. Note that when targeting classes, you might need to put the `!important` selector to override the default Gradio styles. -## Custom JS +## Adding custom JavaScript to your demo -Event listeners have a `_js` argument that can take a Javascript function as a string and treat it just like a Python event listener function. You can pass both a Javascript function and a Python function (in which case the Javascript function is run first) or only Javascript (and set the Python `fn` to `None`). Take a look at the code below: +There are 3 ways to add javascript code to your Gradio demo: +1. You can add JavaScript code as a string or as a filepath to the `js` parameter of the `Blocks` or `Interface` initializer. This will run the JavaScript code when the demo is first loaded. + +Below is an example of adding custom js to show an animated welcome message when the demo first loads. + +$code_blocks_js_load +$demo_blocks_js_load + +Note: You can also supply your custom js code as a file path. For example, if you have a file called `custom.js` in the same directory as your Python script, you can add it to your demo like so: `with gr.Blocks(js="custom.js") as demo:`. Same goes for `Interface` (ex: `gr.Interface(..., js="custom.js")`). + +2. When using `Blocks` and event listeners, events have a `js` argument that can take a JavaScript function as a string and treat it just like a Python event listener function. You can pass both a JavaScript function and a Python function (in which case the JavaScript function is run first) or only Javascript (and set the Python `fn` to `None`). Take a look at the code below: + $code_blocks_js_methods $demo_blocks_js_methods + +1. Lastly, you can add JavaScript code to the `head` param of the `Blocks` initializer. This will add the code to the head of the HTML document. For example, you can add Google Analytics to your demo like so: + + +```python +head = f""" + + +""" + +with gr.Blocks(head=head) as demo: + ...demo code... +``` + +Note: The `head` parameter accepts any HTML tags you would normally insert into the `` of a page. For example, you can also include `` tags to `head`. diff --git a/js/app/src/Index.svelte b/js/app/src/Index.svelte index 6c398c07d4..96b8e09726 100644 --- a/js/app/src/Index.svelte +++ b/js/app/src/Index.svelte @@ -158,12 +158,12 @@ if (parsed_head_html) { for (let head_element of parsed_head_html) { - let newScriptTag = document.createElement("script"); + let newElement = document.createElement(head_element.tagName); Array.from(head_element.attributes).forEach((attr) => { - newScriptTag.setAttribute(attr.name, attr.value); + newElement.setAttribute(attr.name, attr.value); }); - newScriptTag.textContent = head_element.textContent; - document.head.appendChild(newScriptTag); + newElement.textContent = head_element.textContent; + document.head.appendChild(newElement); } } }