diff --git a/.github/workflows/publish-develop-docs.yml b/.github/workflows/publish-develop-docs.yml index b79d3cd2..c2b62d95 100644 --- a/.github/workflows/publish-develop-docs.yml +++ b/.github/workflows/publish-develop-docs.yml @@ -21,3 +21,5 @@ jobs: git config user.email github-actions@github.com cd docs mike deploy --push develop + concurrency: + group: publish-docs diff --git a/.github/workflows/publish-release-docs.yml b/.github/workflows/publish-release-docs.yml index a98e9869..3f24d129 100644 --- a/.github/workflows/publish-release-docs.yml +++ b/.github/workflows/publish-release-docs.yml @@ -21,3 +21,5 @@ jobs: git config user.email github-actions@github.com cd docs mike deploy --push --update-aliases ${{ github.event.release.name }} latest + concurrency: + group: publish-docs diff --git a/CHANGELOG.md b/CHANGELOG.md index 620c1f75..9652b644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,9 @@ Using the following categories, list your changes in this order: ### Security - for vulnerability fixes. - --> + +Don't forget to remove deprecated code on each major release! +--> diff --git a/docs/overrides/home-code-examples/add-interactivity.py b/docs/overrides/home-code-examples/add-interactivity.py index 90976446..f29ba3c8 100644 --- a/docs/overrides/home-code-examples/add-interactivity.py +++ b/docs/overrides/home-code-examples/add-interactivity.py @@ -1,16 +1,14 @@ +# pylint: disable=assignment-from-no-return, unnecessary-lambda from reactpy import component, html, use_state -def filter_videos(videos, search_text): - return None +def filter_videos(*_, **__): ... -def search_input(dictionary, value): - return None +def search_input(*_, **__): ... -def video_list(videos, empty_heading): - return None +def video_list(*_, **__): ... @component @@ -20,7 +18,7 @@ def searchable_video_list(videos): return html._( search_input( - {"on_change": lambda new_text: set_search_text(new_text)}, + {"onChange": lambda new_text: set_search_text(new_text)}, value=search_text, ), video_list( diff --git a/docs/overrides/home-code-examples/create-user-interfaces.py b/docs/overrides/home-code-examples/create-user-interfaces.py index 37776ab1..873b9d88 100644 --- a/docs/overrides/home-code-examples/create-user-interfaces.py +++ b/docs/overrides/home-code-examples/create-user-interfaces.py @@ -1,22 +1,20 @@ from reactpy import component, html -def thumbnail(video): - return None +def thumbnail(*_, **__): ... -def like_button(video): - return None +def like_button(*_, **__): ... @component -def video(video): +def video(data): return html.div( - thumbnail(video), + thumbnail(data), html.a( - {"href": video.url}, - html.h3(video.title), - html.p(video.description), + {"href": data.url}, + html.h3(data.title), + html.p(data.description), ), - like_button(video), + like_button(data), ) diff --git a/docs/overrides/home-code-examples/write-components-with-python.py b/docs/overrides/home-code-examples/write-components-with-python.py index 6af43baa..47e28b68 100644 --- a/docs/overrides/home-code-examples/write-components-with-python.py +++ b/docs/overrides/home-code-examples/write-components-with-python.py @@ -1,6 +1,9 @@ from reactpy import component, html +def video(*_, **__): ... + + @component def video_list(videos, empty_heading): count = len(videos) @@ -11,5 +14,5 @@ def video_list(videos, empty_heading): return html.section( html.h2(heading), - [video(video) for video in videos], + [video(x, key=x.id) for x in videos], ) diff --git a/docs/src/assets/css/home.css b/docs/src/assets/css/home.css index c72e7093..70f05cf2 100644 --- a/docs/src/assets/css/home.css +++ b/docs/src/assets/css/home.css @@ -1,335 +1,337 @@ img.home-logo { - height: 120px; + height: 120px; } .home .row { - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - padding: 6rem 0.8rem; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + padding: 6rem 0.8rem; } .home .row:not(.first, .stripe) { - background: var(--row-bg-color); + background: var(--row-bg-color); } .home .row.stripe { - background: var(--row-stripe-bg-color); - border: 0 solid var(--stripe-border-color); - border-top-width: 1px; - border-bottom-width: 1px; + background: var(--row-stripe-bg-color); + border: 0 solid var(--stripe-border-color); + border-top-width: 1px; + border-bottom-width: 1px; } .home .row.first { - text-align: center; + text-align: center; } .home .row h1 { - max-width: 28rem; - line-height: 1.15; - font-weight: 500; - margin-bottom: 0.55rem; - margin-top: -1rem; + max-width: 28rem; + line-height: 1.15; + font-weight: 500; + margin-bottom: 0.55rem; + margin-top: -1rem; } .home .row.first h1 { - margin-top: 0.55rem; - margin-bottom: -0.75rem; + margin-top: 0.55rem; + margin-bottom: -0.75rem; } .home .row > p { - max-width: 35rem; - line-height: 1.5; - font-weight: 400; + max-width: 35rem; + line-height: 1.5; + font-weight: 400; } .home .row.first > p { - font-size: 32px; - font-weight: 500; + font-size: 32px; + font-weight: 500; } /* Code blocks */ .home .row .tabbed-set { - background: var(--home-tabbed-set-bg-color); - margin: 0; + background: var(--home-tabbed-set-bg-color); + margin: 0; } .home .row .tabbed-content { - padding: 20px 18px; - overflow-x: auto; + padding: 20px 18px; + overflow-x: auto; } .home .row .tabbed-content img { - user-select: none; - -moz-user-select: none; - -webkit-user-drag: none; - -webkit-user-select: none; - -ms-user-select: none; - max-width: 580px; + user-select: none; + -moz-user-select: none; + -webkit-user-drag: none; + -webkit-user-select: none; + -ms-user-select: none; + max-width: 580px; } .home .row .tabbed-content { - -webkit-filter: var(--code-block-filter); - filter: var(--code-block-filter); + -webkit-filter: var(--code-block-filter); + filter: var(--code-block-filter); } /* Code examples */ .home .example-container { - background: radial-gradient( - circle at 0% 100%, - rgb(41 84 147 / 11%) 0%, - rgb(22 89 189 / 4%) 70%, - rgb(48 99 175 / 0%) 80% - ), - radial-gradient( - circle at 100% 100%, - rgb(24 87 45 / 55%) 0%, - rgb(29 61 12 / 4%) 70%, - rgb(94 116 93 / 0%) 80% - ), - radial-gradient( - circle at 100% 0%, - rgba(54, 66, 84, 0.55) 0%, - rgb(102 111 125 / 4%) 70%, - rgba(54, 66, 84, 0) 80% - ), - radial-gradient( - circle at 0% 0%, - rgba(91, 114, 135, 0.55) 0%, - rgb(45 111 171 / 4%) 70%, - rgb(5 82 153 / 0%) 80% - ), - rgb(0, 0, 0) center center/cover no-repeat fixed; - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - align-items: center; - border-radius: 16px; - margin: 30px 0; - max-width: 100%; - grid-column-gap: 20px; - padding-left: 20px; - padding-right: 20px; + background: radial-gradient( + circle at 0% 100%, + rgb(41 84 147 / 11%) 0%, + rgb(22 89 189 / 4%) 70%, + rgb(48 99 175 / 0%) 80% + ), + radial-gradient( + circle at 100% 100%, + rgb(24 87 45 / 55%) 0%, + rgb(29 61 12 / 4%) 70%, + rgb(94 116 93 / 0%) 80% + ), + radial-gradient( + circle at 100% 0%, + rgba(54, 66, 84, 0.55) 0%, + rgb(102 111 125 / 4%) 70%, + rgba(54, 66, 84, 0) 80% + ), + radial-gradient( + circle at 0% 0%, + rgba(91, 114, 135, 0.55) 0%, + rgb(45 111 171 / 4%) 70%, + rgb(5 82 153 / 0%) 80% + ), + rgb(0, 0, 0) center center/cover no-repeat fixed; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: center; + border-radius: 16px; + margin: 30px 0; + max-width: 100%; + grid-column-gap: 20px; + padding-left: 20px; + padding-right: 20px; } .home .demo .white-bg { - background: #fff; - border-radius: 16px; - display: flex; - flex-direction: column; - max-width: 590px; - min-width: -webkit-min-content; - min-width: -moz-min-content; - min-width: min-content; - row-gap: 1rem; - padding: 1rem; + background: #fff; + border-radius: 16px; + display: flex; + flex-direction: column; + max-width: 590px; + min-width: -webkit-min-content; + min-width: -moz-min-content; + min-width: min-content; + row-gap: 1rem; + padding: 1rem; + border: 1px rgb(0 0 0 / 20%) solid; + overflow: hidden; } .home .demo .vid-row { - display: flex; - flex-direction: row; - -moz-column-gap: 12px; - column-gap: 12px; + display: flex; + flex-direction: row; + -moz-column-gap: 12px; + column-gap: 12px; } .home .demo { - color: #000; + color: #000; } .home .demo .vid-thumbnail { - background: radial-gradient( - circle at 0% 100%, - rgb(41 84 147 / 55%) 0%, - rgb(22 89 189 / 4%) 70%, - rgb(48 99 175 / 0%) 80% - ), - radial-gradient( - circle at 100% 100%, - rgb(24 63 87 / 55%) 0%, - rgb(29 61 12 / 4%) 70%, - rgb(94 116 93 / 0%) 80% - ), - radial-gradient( - circle at 100% 0%, - rgba(54, 66, 84, 0.55) 0%, - rgb(102 111 125 / 4%) 70%, - rgba(54, 66, 84, 0) 80% - ), - radial-gradient( - circle at 0% 0%, - rgba(91, 114, 135, 0.55) 0%, - rgb(45 111 171 / 4%) 70%, - rgb(5 82 153 / 0%) 80% - ), - rgb(0, 0, 0) center center/cover no-repeat fixed; - width: 9rem; - aspect-ratio: 16 / 9; - border-radius: 8px; - display: flex; - justify-content: center; - align-items: center; + background: radial-gradient( + circle at 0% 100%, + rgb(41 84 147 / 55%) 0%, + rgb(22 89 189 / 4%) 70%, + rgb(48 99 175 / 0%) 80% + ), + radial-gradient( + circle at 100% 100%, + rgb(24 63 87 / 55%) 0%, + rgb(29 61 12 / 4%) 70%, + rgb(94 116 93 / 0%) 80% + ), + radial-gradient( + circle at 100% 0%, + rgba(54, 66, 84, 0.55) 0%, + rgb(102 111 125 / 4%) 70%, + rgba(54, 66, 84, 0) 80% + ), + radial-gradient( + circle at 0% 0%, + rgba(91, 114, 135, 0.55) 0%, + rgb(45 111 171 / 4%) 70%, + rgb(5 82 153 / 0%) 80% + ), + rgb(0, 0, 0) center center/cover no-repeat fixed; + width: 9rem; + aspect-ratio: 16 / 9; + border-radius: 8px; + display: flex; + justify-content: center; + align-items: center; } .home .demo .vid-text { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + width: 100%; } .home .demo h2 { - font-size: 18px; - line-height: 1.375; - margin: 0; - text-align: left; - font-weight: 700; + font-size: 18px; + line-height: 1.375; + margin: 0; + text-align: left; + font-weight: 700; } .home .demo h3 { - font-size: 16px; - line-height: 1.25; - margin: 0; + font-size: 16px; + line-height: 1.25; + margin: 0; } .home .demo p { - font-size: 14px; - line-height: 1.375; - margin: 0; + font-size: 14px; + line-height: 1.375; + margin: 0; } .home .demo .browser-nav-url { - background: rgba(153, 161, 179, 0.2); - border-radius: 9999px; - font-size: 14px; - color: grey; - display: flex; - align-items: center; - justify-content: center; - -moz-column-gap: 5px; - column-gap: 5px; + background: rgba(153, 161, 179, 0.2); + border-radius: 9999px; + font-size: 14px; + color: grey; + display: flex; + align-items: center; + justify-content: center; + -moz-column-gap: 5px; + column-gap: 5px; } .home .demo .browser-navbar { - margin: -1rem; - margin-bottom: 0; - padding: 0.75rem 1rem; - border-bottom: 1px solid darkgrey; + margin: -1rem; + margin-bottom: 0; + padding: 0.75rem 1rem; + border-bottom: 1px solid darkgrey; } .home .demo .browser-viewport { - background: #fff; - border-radius: 16px; - display: flex; - flex-direction: column; - row-gap: 1rem; - height: 400px; - overflow-y: scroll; - margin: -1rem; - padding: 1rem; + background: #fff; + border-radius: 16px; + display: flex; + flex-direction: column; + row-gap: 1rem; + height: 400px; + overflow-y: scroll; + margin: -1rem; + padding: 1rem; } .home .demo .browser-viewport .search-header > h1 { - color: #000; - text-align: left; - font-size: 24px; - margin: 0; + color: #000; + text-align: left; + font-size: 24px; + margin: 0; } .home .demo .browser-viewport .search-header > p { - text-align: left; - font-size: 16px; - margin: 10px 0; + text-align: left; + font-size: 16px; + margin: 10px 0; } .home .demo .search-bar input { - width: 100%; - background: rgba(153, 161, 179, 0.2); - border-radius: 9999px; - padding-left: 40px; - padding-right: 40px; - height: 40px; - color: #000; + width: 100%; + background: rgba(153, 161, 179, 0.2); + border-radius: 9999px; + padding-left: 40px; + padding-right: 40px; + height: 40px; + color: #000; } .home .demo .search-bar svg { - height: 40px; - position: absolute; - transform: translateX(75%); + height: 40px; + position: absolute; + transform: translateX(75%); } .home .demo .search-bar { - position: relative; + position: relative; } /* Desktop Styling */ @media screen and (min-width: 60em) { - .home .row { - text-align: center; - } - .home .row > p { - font-size: 21px; - } - .home .row > h1 { - font-size: 52px; - } - .home .row .pop-left { - margin-left: -20px; - margin-right: 0; - margin-top: -20px; - margin-bottom: -20px; - } - .home .row .pop-right { - margin-left: 0px; - margin-right: 0px; - margin-top: -20px; - margin-bottom: -20px; - } + .home .row { + text-align: center; + } + .home .row > p { + font-size: 21px; + } + .home .row > h1 { + font-size: 52px; + } + .home .row .pop-left { + margin-left: -20px; + margin-right: 0; + margin-top: -20px; + margin-bottom: -20px; + } + .home .row .pop-right { + margin-left: 0px; + margin-right: 0px; + margin-top: -20px; + margin-bottom: -20px; + } } /* Mobile Styling */ @media screen and (max-width: 60em) { - .home .row { - padding: 4rem 0.8rem; - } - .home .row > h1, - .home .row > p { - padding-left: 1rem; - padding-right: 1rem; - } - .home .row.first { - padding-top: 2rem; - } - .home-btns { - width: 100%; - display: grid; - grid-gap: 0.5rem; - gap: 0.5rem; - } - .home .example-container { - display: flex; - flex-direction: column; - row-gap: 20px; - width: 100%; - justify-content: center; - border-radius: 0; - padding: 1rem 0; - } - .home .row { - padding-left: 0; - padding-right: 0; - } - .home .tabbed-set { - width: 100%; - border-radius: 0; - } - .home .demo { - width: 100%; - display: flex; - justify-content: center; - } - .home .demo > .white-bg { - width: 80%; - max-width: 80%; - } + .home .row { + padding: 4rem 0.8rem; + } + .home .row > h1, + .home .row > p { + padding-left: 1rem; + padding-right: 1rem; + } + .home .row.first { + padding-top: 2rem; + } + .home-btns { + width: 100%; + display: grid; + grid-gap: 0.5rem; + gap: 0.5rem; + } + .home .example-container { + display: flex; + flex-direction: column; + row-gap: 20px; + width: 100%; + justify-content: center; + border-radius: 0; + padding: 1rem 0; + } + .home .row { + padding-left: 0; + padding-right: 0; + } + .home .tabbed-set { + width: 100%; + border-radius: 0; + } + .home .demo { + width: 100%; + display: flex; + justify-content: center; + } + .home .demo > .white-bg { + width: 80%; + max-width: 80%; + } } diff --git a/docs/src/assets/img/add-interactivity.png b/docs/src/assets/img/add-interactivity.png index e5e24d29..009b52ac 100644 Binary files a/docs/src/assets/img/add-interactivity.png and b/docs/src/assets/img/add-interactivity.png differ diff --git a/docs/src/assets/img/create-user-interfaces.png b/docs/src/assets/img/create-user-interfaces.png index 13abd064..06f6ea0c 100644 Binary files a/docs/src/assets/img/create-user-interfaces.png and b/docs/src/assets/img/create-user-interfaces.png differ diff --git a/docs/src/assets/img/write-components-with-python.png b/docs/src/assets/img/write-components-with-python.png index ba34cdf9..380d2c3a 100644 Binary files a/docs/src/assets/img/write-components-with-python.png and b/docs/src/assets/img/write-components-with-python.png differ diff --git a/docs/src/reference/components.md b/docs/src/reference/components.md index aaeabba7..1b7ae9b2 100644 --- a/docs/src/reference/components.md +++ b/docs/src/reference/components.md @@ -14,6 +14,8 @@ This allows you to embedded any number of client-side PyScript components within {% include-markdown "../reference/template-tag.md" start="" end="" %} +{% include-markdown "../reference/template-tag.md" start="" end="" %} + === "components.py" ```python diff --git a/docs/src/reference/decorators.md b/docs/src/reference/decorators.md index 6a0345da..962ec1e6 100644 --- a/docs/src/reference/decorators.md +++ b/docs/src/reference/decorators.md @@ -12,7 +12,7 @@ Decorator functions can be used within your `components.py` to help simplify dev You can limit component access to users that pass a test function by using this decorator. -This decorator is inspired by Django's [`user_passes_test`](http://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.decorators.user_passes_test) decorator, but works with ReactPy components. +This decorator is inspired by Django's [`user_passes_test`](http://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.decorators.user_passes_test) decorator, but this one works with ReactPy components. === "components.py" diff --git a/docs/src/reference/hooks.md b/docs/src/reference/hooks.md index 6d7da90f..43c4641b 100644 --- a/docs/src/reference/hooks.md +++ b/docs/src/reference/hooks.md @@ -400,7 +400,7 @@ This is often used to create chat systems, synchronize data between components, In these cases, you can use the `#!python use_channel_layer` hook to receive a signal within your component, and then use the `#!python get_channel_layer().send(...)` to send the signal. - In the example below, the sender will send a signal every time `#!python ExampleModel` is saved. Then, when the receiver component gets this signal, it explicitly calls `#!python set_message(...)` to trigger a re-render. + In the example below, the sender will signal every time `#!python ExampleModel` is saved. Then, when the receiver gets this signal, it explicitly calls `#!python set_message(...)` to trigger a re-render. === "signals.py" @@ -522,7 +522,7 @@ You can expect this hook to provide strings such as `http://example.com`. Shortcut that returns the root component's `#!python id` from the WebSocket or HTTP connection. -The root ID is currently a randomly generated `#!python uuid4` (unique across all root component). +The root ID is a randomly generated `#!python uuid4`. It is notable to mention that it is persistent across the current connection. The `uuid` is reset when the page is refreshed. This is useful when used in combination with [`#!python use_channel_layer`](#use-channel-layer) to send messages to a specific component instance, and/or retain a backlog of messages in case that component is disconnected via `#!python use_channel_layer( ... , group_discard=False)`. diff --git a/docs/src/reference/html.md b/docs/src/reference/html.md index fd63c033..3fe3fe46 100644 --- a/docs/src/reference/html.md +++ b/docs/src/reference/html.md @@ -10,11 +10,11 @@ We supply some pre-generated that HTML nodes can be used to help simplify develo ## PyScript -Primitive HTML tag that is leveraged by [`reactpy_django.components.pyscript_component`](./components.md#pyscript-component). +PyScript code block. The text content of this tag are executed within the PyScript interpreter. This can be used as an alternative to the `#!python reactpy.html.script`. -This can be used as an alternative to the `#!python reactpy.html.script` tag to execute JavaScript and run client-side Python code. +This is a primitive HTML tag that is leveraged by [`reactpy_django.components.pyscript_component`](./components.md#pyscript-component). -Additionally, this tag functions identically to any other tag contained within `#!python reactpy.html`, and can be used in the same way. +The `pyscript` tag functions identically to HTML tags contained within `#!python reactpy.html`. === "components.py" diff --git a/docs/src/reference/template-tag.md b/docs/src/reference/template-tag.md index 434c81d0..197a3b29 100644 --- a/docs/src/reference/template-tag.md +++ b/docs/src/reference/template-tag.md @@ -10,7 +10,7 @@ Django template tags can be used within your HTML templates to provide ReactPy f ## Component -This template tag can be used to insert any number of ReactPy components onto your page. +This template tag can be used to insert any number of **server-side** ReactPy components onto your page. Each component loaded via this template tag will receive a dedicated WebSocket connection to the server. @@ -159,9 +159,10 @@ This template tag can be used to insert any number of **client-side** ReactPy co By default, the only dependencies available are the Python standard library, `pyscript`, `pyodide`, `reactpy` core. -Your PyScript component file requires a `#!python def root()` component to function as the entry point. +The entire file path provided is loaded directly into the browser, and must have a `#!python def root()` component to act as the entry point. + !!! warning "Pitfall" @@ -169,6 +170,8 @@ Your PyScript component file requires a `#!python def root()` component to funct As a result of running client-side, Python packages within your local environment (such as those installed via `pip install ...`) are **not accessible** within PyScript components. + + === "my_template.html" ```jinja diff --git a/docs/src/reference/utils.md b/docs/src/reference/utils.md index 6590012c..5402cc85 100644 --- a/docs/src/reference/utils.md +++ b/docs/src/reference/utils.md @@ -14,7 +14,7 @@ Utility functions provide various miscellaneous functionality for advanced use c ## Register Iframe -This function is used register a view as an `#!python iframe` with ReactPy. +This function is used register a Django view as a ReactPy `#!python iframe`. It is mandatory to use this function alongside [`view_to_iframe`](../reference/components.md#view-to-iframe). diff --git a/src/reactpy_django/utils.py b/src/reactpy_django/utils.py index 48559e84..401cf724 100644 --- a/src/reactpy_django/utils.py +++ b/src/reactpy_django/utils.py @@ -334,7 +334,7 @@ def django_query_postprocessor( "One of the following may have occurred:\n" " - You are using a non-Django ORM.\n" " - You are attempting to use `use_query` to fetch non-ORM data.\n\n" - "If these situations seem correct, you may want to consider disabling the postprocessor." + "If these situations apply, you may want to disable the postprocessor." ) return data