diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 5b5bcf6b..4db0842e 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -20,6 +20,16 @@ jobs: - uses: actions/setup-python@v4 with: python-version: 3.x - - run: pip install -r requirements/build-docs.txt - - run: linkcheckMarkdown docs/ -v -r - - run: mkdocs build --strict + - name: Check docs build + run: | + pip install -r requirements/build-docs.txt + linkcheckMarkdown docs/ -v -r + mkdocs build --strict + - name: Check docs examples + run: | + pip install -r requirements/check-types.txt + pip install -r requirements/check-style.txt + mypy --show-error-codes docs/python/ + black docs/python/ --check + isort docs/python/ --check-only + flake8 docs/python/ diff --git a/.gitignore b/.gitignore index 2477a484..90028709 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ settings.json *$py.class # Distribution / packaging +build/ .Python build/ develop-eggs/ dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ce7c1daa..248a8bd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), @@ -24,7 +36,52 @@ Using the following categories, list your changes in this order: - Nothing (yet) -## [2.2.1] - 2022-01-09 +## [3.0.0] - 2023-03-08 + +???+ note + + This is Django-IDOM's biggest update yet! + + To upgrade from previous version you will need to... + + 1. Install `django-idom >= 3.0.0` + 2. Run `idom rewrite-keys ` and `idom rewrite-camel-case-props ` to update your `idom.html.*` calls to the new syntax + 3. Run `python manage.py migrate` to create the new Django-IDOM database entries + +### Added + +- The `idom` client will automatically configure itself to debug mode depending on `settings.py:DEBUG`. +- `use_connection` hook for returning the browser's active `Connection`. +- `IDOM_CACHE` is now configurable within `settings.py` to whatever cache name you wish. + +### Changed + +- It is now mandatory to run `manage.py migrate` after installing IDOM. +- Bumped the minimum IDOM version to 1.0.0. Due to IDOM 1.0.0, `idom.html.*`... + - HTML properties can now be `snake_case`. For example `className` now becomes `class_name`. + - `key=...` is now declared within the props `dict` (rather than as a `kwarg`). +- The `component` template tag now supports both positional and keyword arguments. +- The `component` template tag now supports non-serializable arguments. +- `IDOM_WS_MAX_RECONNECT_TIMEOUT` setting has been renamed to `IDOM_RECONNECT_MAX`. + +### Removed + +- `django_idom.hooks.use_websocket` has been removed. The similar replacement is `django_idom.hooks.use_connection`. +- `django_idom.types.IdomWebsocket` has been removed. The similar replacement is `django_idom.types.Connection`. +- `settings.py:CACHE['idom']` is no longer used by default. The name of the cache back-end must now be specified with the `IDOM_CACHE` setting. + +### Fixed + +- `view_to_component` will now retain the contents of a `` tag when rendering. +- React client is now set to `production` rather than `development`. +- `use_query` will now utilize `field.related_name` when postprocessing many-to-one relationships. + +### Security + +- Fixed a potential method of component template tag argument spoofing. +- Exception information will no longer be displayed on the page, based on the value of `settings.py:DEBUG`. + +## [2.2.1] - 2023-01-09 ### Fixed @@ -191,7 +248,8 @@ Using the following categories, list your changes in this order: - Support for IDOM within the Django -[unreleased]: https://github.com/idom-team/django-idom/compare/2.2.1...HEAD +[unreleased]: https://github.com/idom-team/django-idom/compare/3.0.0...HEAD +[3.0.0]: https://github.com/idom-team/django-idom/compare/2.2.1...3.0.0 [2.2.1]: https://github.com/idom-team/django-idom/compare/2.2.0...2.2.1 [2.2.0]: https://github.com/idom-team/django-idom/compare/2.1.0...2.2.0 [2.1.0]: https://github.com/idom-team/django-idom/compare/2.0.1...2.1.0 diff --git a/README.md b/README.md index f45f1242..57c3ac1e 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ -# Django IDOM · [![Tests](https://github.com/idom-team/django-idom/workflows/Test/badge.svg?event=push)](https://github.com/idom-team/django-idom/actions?query=workflow%3ATest) [![PyPI Version](https://img.shields.io/pypi/v/django-idom.svg?label=PyPI)](https://pypi.python.org/pypi/django-idom) [![License](https://img.shields.io/badge/License-MIT-purple.svg)](https://github.com/idom-team/django-idom/blob/main/LICENSE) [![Docs](https://img.shields.io/website?down_message=offline&label=Docs&logo=read%20the%20docs&logoColor=white&up_message=online&url=https%3A%2F%2Fidom-team.github.io%2Fdjango-idom%2F)](https://idom-team.github.io/django-idom/) +# Django-IDOM · [![Tests](https://github.com/idom-team/django-idom/workflows/Test/badge.svg?event=push)](https://github.com/idom-team/django-idom/actions?query=workflow%3ATest) [![PyPI Version](https://img.shields.io/pypi/v/django-idom.svg?label=PyPI)](https://pypi.python.org/pypi/django-idom) [![License](https://img.shields.io/badge/License-MIT-purple.svg)](https://github.com/idom-team/django-idom/blob/main/LICENSE) [![Docs](https://img.shields.io/website?down_message=offline&label=Docs&logo=read%20the%20docs&logoColor=white&up_message=online&url=https%3A%2F%2Fidom-team.github.io%2Fdjango-idom%2F)](https://idom-team.github.io/django-idom/) Django-IDOM connects your Python project to a ReactJS front-end, allowing you to create **interactive websites without needing JavaScript!** -Following ReactJS styling, web elements are combined into [reusable "components"](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/your-first-components/index.html#parametrizing-components). These components can utilize [hooks](https://idom-docs.herokuapp.com/docs/reference/hooks-api.html) and [events](https://idom-docs.herokuapp.com/docs/guides/adding-interactivity/responding-to-events/index.html#async-event-handlers) to create infinitely complex web pages. +Following ReactJS styling, web elements are combined into [reusable "components"](https://reactpy.dev/docs/guides/creating-interfaces/your-first-components/index.html#parametrizing-components). These components can utilize [hooks](https://reactpy.dev/docs/reference/hooks-api.html) and [events](https://reactpy.dev/docs/guides/adding-interactivity/responding-to-events/index.html#async-event-handlers) to create infinitely complex web pages. -When needed, IDOM can [use components directly from NPM](https://idom-docs.herokuapp.com/docs/guides/escape-hatches/javascript-components.html#dynamically-loaded-components). For additional flexibility, components can also be [fully developed in JavaScript](https://idom-docs.herokuapp.com/docs/guides/escape-hatches/javascript-components.html#custom-javascript-components). +When needed, IDOM can [use components directly from NPM](https://reactpy.dev/docs/guides/escape-hatches/javascript-components.html#dynamically-loaded-components). For additional flexibility, components can also be [fully developed in JavaScript](https://reactpy.dev/docs/guides/escape-hatches/javascript-components.html#custom-javascript-components). Any Python web framework with Websockets can support IDOM. See below for what frameworks are supported out of the box. | Supported Frameworks | Supported Frameworks (External) | | --- | --- | -| [`Flask`, `FastAPI`, `Sanic`, `Tornado`](https://idom-docs.herokuapp.com/docs/guides/getting-started/installing-idom.html#officially-supported-servers) | [`Django`](https://github.com/idom-team/django-idom), [`Plotly-Dash`](https://github.com/idom-team/idom-dash), [`Jupyter`](https://github.com/idom-team/idom-jupyter) | +| [`Flask`, `FastAPI`, `Sanic`, `Tornado`](https://reactpy.dev/docs/guides/getting-started/installing-idom.html#officially-supported-servers) | [`Django`](https://github.com/idom-team/django-idom), [`Plotly-Dash`](https://github.com/idom-team/idom-dash), [`Jupyter`](https://github.com/idom-team/idom-jupyter) | @@ -46,7 +46,7 @@ def hello_world(recipient: str): In your **Django app**'s HTML template, you can now embed your IDOM component using the `component` template tag. Within this tag, you will need to type in your dotted path to the component function as the first argument. -Additionally, you can pass in keyword arguments into your component function. For example, after reading the code below, pay attention to how the function definition for `hello_world` (_in the previous example_) accepts a `recipient` argument. +Additionally, you can pass in `args` and `kwargs` into your component function. For example, after reading the code below, pay attention to how the function definition for `hello_world` (_in the previous example_) accepts a `recipient` argument. diff --git a/docs/includes/examples.md b/docs/includes/examples.md deleted file mode 100644 index 6db558c0..00000000 --- a/docs/includes/examples.md +++ /dev/null @@ -1,10 +0,0 @@ - - -```python -from django.db import models - -class TodoItem(models.Model): - text = models.CharField(max_length=255) -``` - - diff --git a/docs/includes/pr.md b/docs/includes/pr.md new file mode 100644 index 00000000..c701e8e2 --- /dev/null +++ b/docs/includes/pr.md @@ -0,0 +1,3 @@ +Now, you can create/modify the Django-IDOM source code, and Pull Request (PR) your changes to our GitHub repository. + +To learn how to create GitHub PRs, [click here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 00000000..e70aa10c --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block content %} +{{ super() }} + +{% if git_page_authors %} +
+ + Authors: {{ git_page_authors | default('enable mkdocs-git-authors-plugin') }} + +
+{% endif %} +{% endblock %} diff --git a/docs/python/__init__.py b/docs/python/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/python/auth-required-attribute.py b/docs/python/auth-required-attribute.py new file mode 100644 index 00000000..f88cd064 --- /dev/null +++ b/docs/python/auth-required-attribute.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.decorators import auth_required + + +@component +@auth_required(auth_attribute="is_staff") +def my_component(): + return html.div("I am logged in!") diff --git a/docs/python/auth-required-component-fallback.py b/docs/python/auth-required-component-fallback.py new file mode 100644 index 00000000..4ad205ad --- /dev/null +++ b/docs/python/auth-required-component-fallback.py @@ -0,0 +1,14 @@ +from idom import component, html + +from django_idom.decorators import auth_required + + +@component +def my_component_fallback(): + return html.div("I am NOT logged in!") + + +@component +@auth_required(fallback=my_component_fallback) +def my_component(): + return html.div("I am logged in!") diff --git a/docs/python/auth-required-custom-attribute-model.py b/docs/python/auth-required-custom-attribute-model.py new file mode 100644 index 00000000..cecb6fa4 --- /dev/null +++ b/docs/python/auth-required-custom-attribute-model.py @@ -0,0 +1,7 @@ +from django.contrib.auth.models import AbstractBaseUser + + +class CustomUserModel(AbstractBaseUser): + @property + def is_really_cool(self): + return True diff --git a/docs/python/auth-required-custom-attribute.py b/docs/python/auth-required-custom-attribute.py new file mode 100644 index 00000000..336af9c5 --- /dev/null +++ b/docs/python/auth-required-custom-attribute.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.decorators import auth_required + + +@component +@auth_required(auth_attribute="is_really_cool") +def my_component(): + return html.div("I am logged in!") diff --git a/docs/python/auth-required-vdom-fallback.py b/docs/python/auth-required-vdom-fallback.py new file mode 100644 index 00000000..80ceb5d7 --- /dev/null +++ b/docs/python/auth-required-vdom-fallback.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.decorators import auth_required + + +@component +@auth_required(fallback=html.div("I am NOT logged in!")) +def my_component(): + return html.div("I am logged in!") diff --git a/docs/python/auth-required.py b/docs/python/auth-required.py new file mode 100644 index 00000000..fe815f3a --- /dev/null +++ b/docs/python/auth-required.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.decorators import auth_required + + +@component +@auth_required +def my_component(): + return html.div("I am logged in!") diff --git a/docs/python/configure-asgi.py b/docs/python/configure-asgi.py new file mode 100644 index 00000000..46260bb7 --- /dev/null +++ b/docs/python/configure-asgi.py @@ -0,0 +1,27 @@ +import os + +from django.core.asgi import get_asgi_application + + +# Ensure DJANGO_SETTINGS_MODULE is set properly based on your project name! +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_project.settings") + +# Fetch ASGI application before importing dependencies that require ORM models. +django_asgi_app = get_asgi_application() + + +from channels.auth import AuthMiddlewareStack # noqa: E402 +from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402 +from channels.sessions import SessionMiddlewareStack # noqa: E402 + +from django_idom import IDOM_WEBSOCKET_PATH # noqa: E402 + + +application = ProtocolTypeRouter( + { + "http": django_asgi_app, + "websocket": SessionMiddlewareStack( + AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH])) + ), + } +) diff --git a/docs/python/configure-channels.py b/docs/python/configure-channels.py new file mode 100644 index 00000000..337a922f --- /dev/null +++ b/docs/python/configure-channels.py @@ -0,0 +1,5 @@ +INSTALLED_APPS = [ + "daphne", + ..., +] +ASGI_APPLICATION = "example_project.asgi.application" diff --git a/docs/python/configure-installed-apps.py b/docs/python/configure-installed-apps.py new file mode 100644 index 00000000..f1d7860a --- /dev/null +++ b/docs/python/configure-installed-apps.py @@ -0,0 +1,4 @@ +INSTALLED_APPS = [ + "django_idom", + ..., +] diff --git a/docs/python/configure-urls.py b/docs/python/configure-urls.py new file mode 100644 index 00000000..db3a6780 --- /dev/null +++ b/docs/python/configure-urls.py @@ -0,0 +1,7 @@ +from django.urls import include, path + + +urlpatterns = [ + path("idom/", include("django_idom.http.urls")), + ..., +] diff --git a/docs/python/django-css-external-link.py b/docs/python/django-css-external-link.py new file mode 100644 index 00000000..22018a31 --- /dev/null +++ b/docs/python/django-css-external-link.py @@ -0,0 +1,11 @@ +from idom import component, html + + +@component +def my_component(): + return html.div( + html.link( + {"rel": "stylesheet", "href": "https://example.com/external-styles.css"} + ), + html.button("My Button!"), + ) diff --git a/docs/python/django-css-local-link.py b/docs/python/django-css-local-link.py new file mode 100644 index 00000000..b6fc1e53 --- /dev/null +++ b/docs/python/django-css-local-link.py @@ -0,0 +1,10 @@ +from django.templatetags.static import static +from idom import component, html + + +@component +def my_component(): + return html.div( + html.link({"rel": "stylesheet", "href": static("css/buttons.css")}), + html.button("My Button!"), + ) diff --git a/docs/python/django-css.py b/docs/python/django-css.py new file mode 100644 index 00000000..1f5a31da --- /dev/null +++ b/docs/python/django-css.py @@ -0,0 +1,11 @@ +from idom import component, html + +from django_idom.components import django_css + + +@component +def my_component(): + return html.div( + django_css("css/buttons.css"), + html.button("My Button!"), + ) diff --git a/docs/python/django-js-local-script.py b/docs/python/django-js-local-script.py new file mode 100644 index 00000000..58021edb --- /dev/null +++ b/docs/python/django-js-local-script.py @@ -0,0 +1,10 @@ +from django.templatetags.static import static +from idom import component, html + + +@component +def my_component(): + return html.div( + html.script({"src": static("js/scripts.js")}), + html.button("My Button!"), + ) diff --git a/docs/python/django-js-remote-script.py b/docs/python/django-js-remote-script.py new file mode 100644 index 00000000..c1e734e0 --- /dev/null +++ b/docs/python/django-js-remote-script.py @@ -0,0 +1,9 @@ +from idom import component, html + + +@component +def my_component(): + return html.div( + html.script({"src": "https://example.com/external-scripts.js"}), + html.button("My Button!"), + ) diff --git a/docs/python/django-js.py b/docs/python/django-js.py new file mode 100644 index 00000000..fd7197ba --- /dev/null +++ b/docs/python/django-js.py @@ -0,0 +1,11 @@ +from idom import component, html + +from django_idom.components import django_js + + +@component +def my_component(): + return html.div( + html.button("My Button!"), + django_js("js/scripts.js"), + ) diff --git a/docs/python/django-query-postprocessor.py b/docs/python/django-query-postprocessor.py new file mode 100644 index 00000000..36994a28 --- /dev/null +++ b/docs/python/django-query-postprocessor.py @@ -0,0 +1,24 @@ +from example.models import TodoItem +from idom import component + +from django_idom.hooks import use_query +from django_idom.types import QueryOptions +from django_idom.utils import django_query_postprocessor + + +def get_items(): + return TodoItem.objects.all() + + +@component +def todo_list(): + # These `QueryOptions` are functionally equivalent to Django-IDOM's default values + item_query = use_query( + QueryOptions( + postprocessor=django_query_postprocessor, + postprocessor_kwargs={"many_to_many": True, "many_to_one": True}, + ), + get_items, + ) + + return item_query.data diff --git a/docs/python/example/__init__.py b/docs/python/example/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs/python/example/models.py b/docs/python/example/models.py new file mode 100644 index 00000000..ec98be92 --- /dev/null +++ b/docs/python/example/models.py @@ -0,0 +1,5 @@ +from django.db.models import CharField, Model + + +class TodoItem(Model): + text: CharField = CharField(max_length=255) diff --git a/docs/python/example/urls.py b/docs/python/example/urls.py new file mode 100644 index 00000000..e3d7175d --- /dev/null +++ b/docs/python/example/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from example import views + + +urlpatterns = [ + path("example/", views.index), +] diff --git a/docs/python/example/views.py b/docs/python/example/views.py new file mode 100644 index 00000000..a8ed7fdb --- /dev/null +++ b/docs/python/example/views.py @@ -0,0 +1,5 @@ +from django.shortcuts import render + + +def index(request): + return render(request, "my-template.html") diff --git a/docs/python/settings.py b/docs/python/settings.py new file mode 100644 index 00000000..1c3c809c --- /dev/null +++ b/docs/python/settings.py @@ -0,0 +1,15 @@ +# IDOM requires a multiprocessing-safe and thread-safe cache. +IDOM_CACHE = "default" + +# IDOM requires a multiprocessing-safe and thread-safe database. +IDOM_DATABASE = "default" + +# Maximum seconds between reconnection attempts before giving up. +# Use `0` to prevent component reconnection. +IDOM_RECONNECT_MAX = 259200 + +# The URL for IDOM to serve the component rendering websocket +IDOM_WEBSOCKET_URL = "idom/" + +# Dotted path to the default `django_idom.hooks.use_query` postprocessor function, or `None` +IDOM_DEFAULT_QUERY_POSTPROCESSOR = "django_idom.utils.django_query_postprocessor" diff --git a/docs/python/template-tag-args-kwargs.py b/docs/python/template-tag-args-kwargs.py new file mode 100644 index 00000000..0eb96bd6 --- /dev/null +++ b/docs/python/template-tag-args-kwargs.py @@ -0,0 +1,6 @@ +from idom import component + + +@component +def frog_greeter(number, name, species=""): + return f"Hello #{number}, {name} the {species}!" diff --git a/docs/python/template-tag-bad-view.py b/docs/python/template-tag-bad-view.py new file mode 100644 index 00000000..a798abb0 --- /dev/null +++ b/docs/python/template-tag-bad-view.py @@ -0,0 +1,6 @@ +from django.shortcuts import render + + +def example_view(request): + context_vars = {"dont_do_this": "example_project.my_app.components.hello_world"} + return render(request, "my-template.html", context_vars) diff --git a/docs/python/use-connection.py b/docs/python/use-connection.py new file mode 100644 index 00000000..cbee5c04 --- /dev/null +++ b/docs/python/use-connection.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.hooks import use_connection + + +@component +def my_component(): + my_connection = use_connection() + return html.div(str(my_connection)) diff --git a/docs/python/use-location.py b/docs/python/use-location.py new file mode 100644 index 00000000..e4c38caf --- /dev/null +++ b/docs/python/use-location.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.hooks import use_location + + +@component +def my_component(): + my_location = use_location() + return html.div(str(my_location)) diff --git a/docs/python/use-mutation-args-kwargs.py b/docs/python/use-mutation-args-kwargs.py new file mode 100644 index 00000000..9df5861f --- /dev/null +++ b/docs/python/use-mutation-args-kwargs.py @@ -0,0 +1,16 @@ +from idom import component + +from django_idom.hooks import use_mutation + + +def example_mutation(value: int, other_value: bool = False): + ... + + +@component +def my_component(): + mutation = use_mutation(example_mutation) + + mutation.execute(123, other_value=True) + + ... diff --git a/docs/python/use-mutation-query-refetch.py b/docs/python/use-mutation-query-refetch.py new file mode 100644 index 00000000..065bb433 --- /dev/null +++ b/docs/python/use-mutation-query-refetch.py @@ -0,0 +1,45 @@ +from example.models import TodoItem +from idom import component, html + +from django_idom.hooks import use_mutation, use_query + + +def get_items(): + return TodoItem.objects.all() + + +def add_item(text: str): + TodoItem(text=text).save() + + +@component +def todo_list(): + item_query = use_query(get_items) + item_mutation = use_mutation(add_item, refetch=get_items) + + def submit_event(event): + if event["key"] == "Enter": + item_mutation.execute(text=event["target"]["value"]) + + # Handle all possible query states + if item_query.loading: + rendered_items = html.h2("Loading...") + elif item_query.error or not item_query.data: + rendered_items = html.h2("Error when loading!") + else: + rendered_items = html.ul(html.li(item, key=item) for item in item_query.data) + + # Handle all possible mutation states + if item_mutation.loading: + mutation_status = html.h2("Adding...") + elif item_mutation.error: + mutation_status = html.h2("Error when adding!") + else: + mutation_status = html.h2("Mutation done.") + + return html.div( + html.label("Add an item:"), + html.input({"type": "text", "onKeyDown": submit_event}), + mutation_status, + rendered_items, + ) diff --git a/docs/python/use-mutation-reset.py b/docs/python/use-mutation-reset.py new file mode 100644 index 00000000..cab70156 --- /dev/null +++ b/docs/python/use-mutation-reset.py @@ -0,0 +1,33 @@ +from example.models import TodoItem +from idom import component, html + +from django_idom.hooks import use_mutation + + +def add_item(text: str): + TodoItem(text=text).save() + + +@component +def todo_list(): + item_mutation = use_mutation(add_item) + + def reset_event(event): + item_mutation.reset() + + def submit_event(event): + if event["key"] == "Enter": + item_mutation.execute(text=event["target"]["value"]) + + if item_mutation.loading: + mutation_status = html.h2("Adding...") + elif item_mutation.error: + mutation_status = html.button({"onClick": reset_event}, "Error: Try again!") + else: + mutation_status = html.h2("Mutation done.") + + return html.div( + html.label("Add an item:"), + html.input({"type": "text", "onKeyDown": submit_event}), + mutation_status, + ) diff --git a/docs/python/use-mutation.py b/docs/python/use-mutation.py new file mode 100644 index 00000000..ce762e5f --- /dev/null +++ b/docs/python/use-mutation.py @@ -0,0 +1,29 @@ +from example.models import TodoItem +from idom import component, html + +from django_idom.hooks import use_mutation + + +def add_item(text: str): + TodoItem(text=text).save() + + +@component +def todo_list(): + def submit_event(event): + if event["key"] == "Enter": + item_mutation.execute(text=event["target"]["value"]) + + item_mutation = use_mutation(add_item) + if item_mutation.loading: + mutation_status = html.h2("Adding...") + elif item_mutation.error: + mutation_status = html.h2("Error when adding!") + else: + mutation_status = html.h2("Mutation done.") + + return html.div( + html.label("Add an item:"), + html.input({"type": "text", "onKeyDown": submit_event}), + mutation_status, + ) diff --git a/docs/python/use-origin.py b/docs/python/use-origin.py new file mode 100644 index 00000000..6f274766 --- /dev/null +++ b/docs/python/use-origin.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.hooks import use_origin + + +@component +def my_component(): + my_origin = use_origin() + return html.div(my_origin or "No origin") diff --git a/docs/python/use-query-args.py b/docs/python/use-query-args.py new file mode 100644 index 00000000..670f310b --- /dev/null +++ b/docs/python/use-query-args.py @@ -0,0 +1,18 @@ +from idom import component + +from django_idom.hooks import use_query + + +def example_query(value: int, other_value: bool = False): + ... + + +@component +def my_component(): + query = use_query( + example_query, + 123, + other_value=True, + ) + + return str(query.data) diff --git a/docs/python/use-query-postprocessor-change.py b/docs/python/use-query-postprocessor-change.py new file mode 100644 index 00000000..ba8fc0fa --- /dev/null +++ b/docs/python/use-query-postprocessor-change.py @@ -0,0 +1,32 @@ +from idom import component + +from django_idom.hooks import use_query +from django_idom.types import QueryOptions + + +def my_postprocessor(data, example_kwarg=True): + if example_kwarg: + return data + + return dict(data) + + +def execute_io_intensive_operation(): + """This is an example query function that does something IO intensive.""" + pass + + +@component +def todo_list(): + query = use_query( + QueryOptions( + postprocessor=my_postprocessor, + postprocessor_kwargs={"example_kwarg": False}, + ), + execute_io_intensive_operation, + ) + + if query.loading or query.error: + return None + + return str(query.data) diff --git a/docs/python/use-query-postprocessor-disable.py b/docs/python/use-query-postprocessor-disable.py new file mode 100644 index 00000000..552c5bc9 --- /dev/null +++ b/docs/python/use-query-postprocessor-disable.py @@ -0,0 +1,22 @@ +from idom import component + +from django_idom.hooks import use_query +from django_idom.types import QueryOptions + + +def execute_io_intensive_operation(): + """This is an example query function that does something IO intensive.""" + pass + + +@component +def todo_list(): + query = use_query( + QueryOptions(postprocessor=None), + execute_io_intensive_operation, + ) + + if query.loading or query.error: + return None + + return str(query.data) diff --git a/docs/python/use-query-postprocessor-kwargs.py b/docs/python/use-query-postprocessor-kwargs.py new file mode 100644 index 00000000..05f72e38 --- /dev/null +++ b/docs/python/use-query-postprocessor-kwargs.py @@ -0,0 +1,32 @@ +from example.models import TodoItem +from idom import component + +from django_idom.hooks import use_query +from django_idom.types import QueryOptions + + +def get_model_with_relationships(): + """This is an example query function that gets `MyModel` which has a ManyToMany field, and + additionally other models that have formed a ForeignKey association to `MyModel`. + + ManyToMany Field: `many_to_many_field` + ForeignKey Field: `foreign_key_field_set` + """ + return TodoItem.objects.get(id=1) + + +@component +def todo_list(): + query = use_query( + QueryOptions( + postprocessor_kwargs={"many_to_many": False, "many_to_one": False} + ), + get_model_with_relationships, + ) + + if query.loading or query.error or not query.data: + return None + + # By disabling `many_to_many` and `many_to_one`, accessing these fields will now + # generate a `SynchronousOnlyOperation` exception + return f"{query.data.many_to_many_field} {query.data.foriegn_key_field_set}" diff --git a/docs/python/use-query.py b/docs/python/use-query.py new file mode 100644 index 00000000..df45998c --- /dev/null +++ b/docs/python/use-query.py @@ -0,0 +1,22 @@ +from example.models import TodoItem +from idom import component, html + +from django_idom.hooks import use_query + + +def get_items(): + return TodoItem.objects.all() + + +@component +def todo_list(): + item_query = use_query(get_items) + + if item_query.loading: + rendered_items = html.h2("Loading...") + elif item_query.error or not item_query.data: + rendered_items = html.h2("Error when loading!") + else: + rendered_items = html.ul(html.li(item, key=item) for item in item_query.data) + + return html.div("Rendered items: ", rendered_items) diff --git a/docs/python/use-scope.py b/docs/python/use-scope.py new file mode 100644 index 00000000..f5e2fbf3 --- /dev/null +++ b/docs/python/use-scope.py @@ -0,0 +1,9 @@ +from idom import component, html + +from django_idom.hooks import use_scope + + +@component +def my_component(): + my_scope = use_scope() + return html.div(str(my_scope)) diff --git a/docs/python/vtc-args-kwargs.py b/docs/python/vtc-args-kwargs.py new file mode 100644 index 00000000..74e1c89f --- /dev/null +++ b/docs/python/vtc-args-kwargs.py @@ -0,0 +1,22 @@ +from django.http import HttpResponse +from idom import component, html + +from django_idom.components import view_to_component + + +@view_to_component +def hello_world_view(request, arg1, arg2, key1=None, key2=None): + return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}") + + +@component +def my_component(): + return html.div( + hello_world_view( + None, # Your request object (optional) + "value_1", + "value_2", + key1="abc", + key2="123", + ), + ) diff --git a/docs/python/vtc-cbv-compatibility.py b/docs/python/vtc-cbv-compatibility.py new file mode 100644 index 00000000..5b6730c1 --- /dev/null +++ b/docs/python/vtc-cbv-compatibility.py @@ -0,0 +1,11 @@ +from django.contrib.auth.decorators import user_passes_test +from django.utils.decorators import method_decorator +from django.views.generic import TemplateView + +from django_idom.components import view_to_component + + +@view_to_component(compatibility=True) +@method_decorator(user_passes_test(lambda u: u.is_superuser), name="dispatch") # type: ignore[union-attr] +class ExampleView(TemplateView): + ... diff --git a/docs/python/vtc-cbv.py b/docs/python/vtc-cbv.py new file mode 100644 index 00000000..c8da882f --- /dev/null +++ b/docs/python/vtc-cbv.py @@ -0,0 +1,20 @@ +from django.http import HttpResponse +from django.views import View +from idom import component, html + +from django_idom.components import view_to_component + + +class HelloWorldView(View): + def get(self, request): + return HttpResponse("Hello World!") + + +vtc = view_to_component(HelloWorldView) + + +@component +def my_component(): + return html.div( + vtc(), + ) diff --git a/docs/python/vtc-compatibility.py b/docs/python/vtc-compatibility.py new file mode 100644 index 00000000..d7e384a3 --- /dev/null +++ b/docs/python/vtc-compatibility.py @@ -0,0 +1,16 @@ +from django.http import HttpResponse +from idom import component, html + +from django_idom.components import view_to_component + + +@view_to_component(compatibility=True) +def hello_world_view(request): + return HttpResponse("Hello World!") + + +@component +def my_component(): + return html.div( + hello_world_view(), + ) diff --git a/docs/python/vtc-fbv-compat.py b/docs/python/vtc-fbv-compat.py new file mode 100644 index 00000000..ac527ce0 --- /dev/null +++ b/docs/python/vtc-fbv-compat.py @@ -0,0 +1,9 @@ +from django.contrib.auth.decorators import user_passes_test + +from django_idom.components import view_to_component + + +@view_to_component(compatibility=True) +@user_passes_test(lambda u: u.is_superuser) # type: ignore[union-attr] +def example_view(request): + ... diff --git a/docs/python/vtc-func.py b/docs/python/vtc-func.py new file mode 100644 index 00000000..342e0f39 --- /dev/null +++ b/docs/python/vtc-func.py @@ -0,0 +1,14 @@ +from example.views import example_view +from idom import component, html + +from django_idom.components import view_to_component + + +example_vtc = view_to_component(example_view) + + +@component +def my_component(): + return html.div( + example_vtc(), + ) diff --git a/docs/python/vtc-request.py b/docs/python/vtc-request.py new file mode 100644 index 00000000..bfc32ac2 --- /dev/null +++ b/docs/python/vtc-request.py @@ -0,0 +1,22 @@ +from django.http import HttpRequest, HttpResponse +from idom import component, html + +from django_idom.components import view_to_component + + +example_request = HttpRequest() +example_request.method = "PUT" + + +@view_to_component +def hello_world_view(request): + return HttpResponse(f"Hello World! {request.method}") + + +@component +def my_component(): + return html.div( + hello_world_view( + example_request, + ), + ) diff --git a/docs/python/vtc-strict-parsing.py b/docs/python/vtc-strict-parsing.py new file mode 100644 index 00000000..859a3a43 --- /dev/null +++ b/docs/python/vtc-strict-parsing.py @@ -0,0 +1,16 @@ +from django.http import HttpResponse +from idom import component, html + +from django_idom.components import view_to_component + + +@view_to_component(strict_parsing=False) +def hello_world_view(request): + return HttpResponse(" Hello World ") + + +@component +def my_component(): + return html.div( + hello_world_view(), + ) diff --git a/docs/python/vtc-transforms.py b/docs/python/vtc-transforms.py new file mode 100644 index 00000000..1b1fc9c0 --- /dev/null +++ b/docs/python/vtc-transforms.py @@ -0,0 +1,22 @@ +from django.http import HttpResponse +from idom import component, html + +from django_idom.components import view_to_component + + +def example_transform(vdom): + attributes = vdom.get("attributes") + if attributes and attributes.get("id") == "hello-world": + vdom["children"][0] = "Good Bye World!" + + +@view_to_component(transforms=[example_transform]) +def hello_world_view(request): + return HttpResponse("
Hello World!
") + + +@component +def my_component(): + return html.div( + hello_world_view(), + ) diff --git a/docs/python/vtc.py b/docs/python/vtc.py new file mode 100644 index 00000000..a7a73bbf --- /dev/null +++ b/docs/python/vtc.py @@ -0,0 +1,16 @@ +from django.http import HttpResponse +from idom import component, html + +from django_idom.components import view_to_component + + +@view_to_component +def hello_world_view(request): + return HttpResponse("Hello World!") + + +@component +def my_component(): + return html.div( + hello_world_view(), + ) diff --git a/docs/src/changelog/index.md b/docs/src/changelog/index.md index a4a0f241..20120d5e 100644 --- a/docs/src/changelog/index.md +++ b/docs/src/changelog/index.md @@ -1,11 +1,12 @@ --- hide: - - navigation - toc --- -!!! note "Attribution" +!!! summary "Attribution" {% include-markdown "../../../CHANGELOG.md" start="" end="" %} +--- + {% include-markdown "../../../CHANGELOG.md" start="" %} diff --git a/docs/src/contribute/code.md b/docs/src/contribute/code.md index 2a6b9feb..cf084637 100644 --- a/docs/src/contribute/code.md +++ b/docs/src/contribute/code.md @@ -1,6 +1,16 @@ -???+ tip "Looking to contribute features that are not Django specific?" +## Overview - Everything within the `django-idom` repository must be specific to Django integration. Check out the [IDOM Core documentation](https://idom-docs.herokuapp.com/docs/about/contributor-guide.html) to contribute general features such as: components, hooks, events, and more. +!!! summary + + You will need to set up a Python environment to develop Django-IDOM. + +??? tip "Looking to contribute features that are not Django specific?" + + Everything within the `django-idom` repository must be specific to Django integration. Check out the [IDOM Core documentation](https://reactpy.dev/docs/about/contributor-guide.html) to contribute general features such as: components, hooks, events, and more. + +--- + +## Modifying Code If you plan to make code changes to this repository, you will need to install the following dependencies first: @@ -10,7 +20,7 @@ If you plan to make code changes to this repository, you will need to install th Once done, you should clone this repository: -```bash +```bash linenums="0" git clone https://github.com/idom-team/django-idom.git cd django-idom ``` @@ -20,15 +30,19 @@ Then, by running the command below you can: - Install an editable version of the Python code - Download, build, and install Javascript dependencies -```bash +```bash linenums="0" pip install -e . -r requirements.txt ``` -Finally, to verify that everything is working properly, you can manually run the development webserver. +Finally, to verify that everything is working properly, you can manually run the test webserver. -```bash +```bash linenums="0" cd tests python manage.py runserver ``` Navigate to `http://127.0.0.1:8000` to see if the tests are rendering correctly. + +## GitHub Pull Request + +{% include-markdown "../../includes/pr.md" %} diff --git a/docs/src/contribute/docs.md b/docs/src/contribute/docs.md index d918c4b9..b16d2ec7 100644 --- a/docs/src/contribute/docs.md +++ b/docs/src/contribute/docs.md @@ -1,3 +1,13 @@ +## Overview + +!!! summary + + You will need to set up a Python environment to preview docs changes. + +--- + +## Modifying Docs + If you plan to make changes to this documentation, you will need to install the following dependencies first: - [Python 3.8+](https://www.python.org/downloads/) @@ -5,7 +15,7 @@ If you plan to make changes to this documentation, you will need to install the Once done, you should clone this repository: -```bash +```bash linenums="0" git clone https://github.com/idom-team/django-idom.git cd django-idom ``` @@ -15,14 +25,18 @@ Then, by running the command below you can: - Install an editable version of the documentation - Self-host a test server for the documentation -```bash -pip install -r ./requirements/build-docs.txt --upgrade +```bash linenums="0" +pip install -e . -r requirements.txt --upgrade ``` Finally, to verify that everything is working properly, you can manually run the docs preview webserver. -```bash +```bash linenums="0" mkdocs serve ``` Navigate to `http://127.0.0.1:8000` to view a preview of the documentation. + +## GitHub Pull Request + +{% include-markdown "../../includes/pr.md" %} diff --git a/docs/src/contribute/running-tests.md b/docs/src/contribute/running-tests.md index 8f806e08..c0f20065 100644 --- a/docs/src/contribute/running-tests.md +++ b/docs/src/contribute/running-tests.md @@ -1,3 +1,13 @@ +## Overview + +!!! summary + + You will need to set up a Python environment to run out test suite. + +--- + +## Running Tests + This repository uses [Nox](https://nox.thea.codes/en/stable/) to run tests. For a full test of available scripts run `nox -l`. If you plan to run tests, you will need to install the following dependencies first: @@ -7,20 +17,31 @@ If you plan to run tests, you will need to install the following dependencies fi Once done, you should clone this repository: -```bash +```bash linenums="0" git clone https://github.com/idom-team/django-idom.git cd django-idom -pip install -r ./requirements/test-run.txt --upgrade +pip install -e . -r requirements.txt --upgrade ``` -Then, by running the command below you can run the full test suite: +## Full Test Suite -``` +By running the command below you can run the full test suite: + +```bash linenums="0" nox -s test ``` Or, if you want to run the tests in the foreground: -``` +```bash linenums="0" nox -s test -- --headed ``` + +## Only Django Tests + +Alternatively, if you want to only run Django related tests, you can use the following command: + +```bash linenums="0" +cd tests +python mange.py test +``` diff --git a/docs/src/dictionary.txt b/docs/src/dictionary.txt index 7892a120..c21b4dfd 100644 --- a/docs/src/dictionary.txt +++ b/docs/src/dictionary.txt @@ -25,3 +25,6 @@ idom asgi postfixed postprocessing +serializable +postprocessor +preprocessor diff --git a/docs/src/features/components.md b/docs/src/features/components.md index 9f444d8d..789d655d 100644 --- a/docs/src/features/components.md +++ b/docs/src/features/components.md @@ -1,7 +1,11 @@ -???+ summary +## Overview + +!!! summary Prefabricated components can be used within your `components.py` to help simplify development. +--- + ## View To Component Convert any Django view into a IDOM component by using this decorator. Compatible with [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/). Views can be sync or async. @@ -9,19 +13,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django_idom.components import view_to_component - - @view_to_component - def hello_world_view(request): - return HttpResponse("Hello World!") - - @component - def my_component(): - return html.div( - hello_world_view(), - ) + {% include "../../python/vtc.py" %} ``` ??? example "See Interface" @@ -52,23 +44,13 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "Function Based View" ```python - ... - - @view_to_component(compatibility=True) - @user_passes_test(lambda u: u.is_superuser) - def example_view(request): - ... + {% include "../../python/vtc-fbv-compat.py" %} ``` === "Class Based View" ```python - ... - - @view_to_component(compatibility=True) - @method_decorator(user_passes_test(lambda u: u.is_superuser), name="dispatch") - class ExampleView(TemplateView): - ... + {% include "../../python/vtc-cbv-compatibility.py" %} ``` ??? info "Existing limitations" @@ -77,8 +59,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl - Requires manual intervention to change request methods beyond `GET`. - IDOM events cannot conveniently be attached to converted view HTML. - - Does not currently load any HTML contained with a `` tag - - Has no option to automatically intercept local anchor link (such as `#!html `) click events + - Has no option to automatically intercept local anchor link (such as `#!html `) click events. _Please note these limitations do not exist when using `compatibility` mode._ @@ -89,21 +70,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django.views import View - from django_idom.components import view_to_component - - @view_to_component - class HelloWorldView(View): - def get(self, request): - return HttpResponse("Hello World!") - - @component - def my_component(): - return html.div( - HelloWorldView(), - ) + {% include "../../python/vtc-cbv.py" %} ``` ??? question "How do I transform views from external libraries?" @@ -113,18 +80,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django_idom.components import view_to_component - from some_library.views import example_view - - example_vtc = view_to_component(example_view) - - @component - def my_component(): - return html.div( - example_vtc(), - ) + {% include "../../python/vtc-func.py" %} ``` ??? question "How do I provide `request`, `args`, and `kwargs` to a view?" @@ -136,24 +92,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse, HttpRequest - from django_idom.components import view_to_component - - example_request = HttpRequest() - example_request.method = "PUT" - - @view_to_component - def hello_world_view(request): - return HttpResponse(f"Hello World! {request.method}") - - @component - def my_component(): - return html.div( - hello_world_view( - example_request, - ), - ) + {% include "../../python/vtc-request.py" %} ``` --- @@ -165,28 +104,10 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django_idom.components import view_to_component - - @view_to_component - def hello_world_view(request, arg1, arg2, key1=None, key2=None): - return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}") - - @component - def my_component(): - return html.div( - hello_world_view( - None, # Your request object (optional) - "value_1", - "value_2", - key1="abc", - key2="123", - ), - ) + {% include "../../python/vtc-args-kwargs.py" %} ``` -??? question "How do I use `strict_parseing`, `compatibility`, and `transforms`?" +??? question "How do I use `strict_parsing`, `compatibility`, and `transforms`?" **`strict_parsing`** @@ -199,19 +120,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django_idom.components import view_to_component - - @view_to_component(strict_parsing=False) - def hello_world_view(request): - return HttpResponse(" Hello World ") - - @component - def my_component(): - return html.div( - hello_world_view(), - ) + {% include "../../python/vtc-strict-parsing.py" %} ``` _Note: Best-fit parsing is designed to be similar to how web browsers would handle non-standard or broken HTML._ @@ -229,19 +138,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django_idom.components import view_to_component - - @view_to_component(compatibility=True) - def hello_world_view(request): - return HttpResponse("Hello World!") - - @component - def my_component(): - return html.div( - hello_world_view(), - ) + {% include "../../python/vtc-compatibility.py" %} ``` _Note: By default the `compatibility` iframe is unstyled, and thus won't look pretty until you add some CSS._ @@ -250,7 +147,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl **`transforms`** - After your view has been turned into [VDOM](https://idom-docs.herokuapp.com/docs/reference/specifications.html#vdom) (python dictionaries), `view_to_component` will call your `transforms` functions on every VDOM node. + After your view has been turned into [VDOM](https://reactpy.dev/docs/reference/specifications.html#vdom) (python dictionaries), `view_to_component` will call your `transforms` functions on every VDOM node. This allows you to modify your view prior to rendering. @@ -259,24 +156,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl === "components.py" ```python - from idom import component, html - from django.http import HttpResponse - from django_idom.components import view_to_component - - def example_transform(vdom): - attributes = vdom.get("attributes") - if attributes and attributes.get("id") == "hello-world": - vdom["children"][0] = "Good Bye World!" - - @view_to_component(transforms=[example_transform]) - def hello_world_view(request): - return HttpResponse("
Hello World!
") - - @component - def my_component(): - return html.div( - hello_world_view(), - ) + {% include "../../python/vtc-transforms.py" %} ``` ## Django CSS @@ -286,15 +166,7 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. === "components.py" ```python - from idom import component, html - from django_idom.components import django_css - - @component - def my_component(): - return html.div( - django_css("css/buttons.css"), - html.button("My Button!"), - ) + {% include "../../python/django-css.py" %} ``` ??? example "See Interface" @@ -312,7 +184,7 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. | --- | --- | | `Component` | An IDOM component. | -??? question "Should I put `django_css` at the top of my component?" +??? question "Should I put `django_css` at the top of my HTML?" Yes, if the stylesheet contains styling for your component. @@ -323,15 +195,7 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. Here's an example on what you should avoid doing for Django static files: ```python - from idom import component, html - from django.templatetags.static import static - - @component - def my_component(): - return html.div( - html.link({"rel": "stylesheet", "href": static("css/buttons.css")}), - html.button("My Button!"), - ) + {% include "../../python/django-css-local-link.py" %} ``` ??? question "How do I load external CSS?" @@ -341,14 +205,7 @@ Allows you to defer loading a CSS stylesheet until a component begins rendering. For external CSS, substitute `django_css` with `html.link`. ```python - from idom import component, html - - @component - def my_component(): - return html.div( - html.link({"rel": "stylesheet", "href": "https://example.com/external-styles.css"}), - html.button("My Button!"), - ) + {% include "../../python/django-css-external-link.py" %} ``` ??? question "Why not load my CSS in `#!html `?" @@ -364,15 +221,7 @@ Allows you to defer loading JavaScript until a component begins rendering. This === "components.py" ```python - from idom import component, html - from django_idom.components import django_js - - @component - def my_component(): - return html.div( - html.button("My Button!"), - django_js("js/scripts.js"), - ) + {% include "../../python/django-js.py" %} ``` ??? example "See Interface" @@ -390,7 +239,7 @@ Allows you to defer loading JavaScript until a component begins rendering. This | --- | --- | | `Component` | An IDOM component. | -??? question "Should I put `django_js` at the bottom of my component?" +??? question "Should I put `django_js` at the bottom of my HTML?" Yes, if your scripts are reliant on the contents of the component. @@ -401,15 +250,7 @@ Allows you to defer loading JavaScript until a component begins rendering. This Here's an example on what you should avoid doing for Django static files: ```python - from idom import component, html - from django.templatetags.static import static - - @component - def my_component(): - return html.div( - html.script({"src": static("js/scripts.js")}), - html.button("My Button!"), - ) + {% include "../../python/django-js-local-script.py" %} ``` ??? question "How do I load external JS?" @@ -419,14 +260,7 @@ Allows you to defer loading JavaScript until a component begins rendering. This For external JavaScript, substitute `django_js` with `html.script`. ```python - from idom import component, html - - @component - def my_component(): - return html.div( - html.script({"src": "https://example.com/external-scripts.js"}), - html.button("My Button!"), - ) + {% include "../../python/django-js-remote-script.py" %} ``` ??? question "Why not load my JS in `#!html `?" diff --git a/docs/src/features/decorators.md b/docs/src/features/decorators.md index e643ec77..c7c0a958 100644 --- a/docs/src/features/decorators.md +++ b/docs/src/features/decorators.md @@ -1,28 +1,23 @@ -???+ summary +## Overview + +!!! summary Decorator utilities can be used within your `components.py` to help simplify development. +--- + ## Auth Required -You can limit access to a component to users with a specific `auth_attribute` by using this decorator. +You can limit access to a component to users with a specific `auth_attribute` by using this decorator (with or without parentheses). By default, this decorator checks if the user is logged in, and his/her account has not been deactivated. This decorator is commonly used to selectively render a component only if a user [`is_staff`](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.User.is_staff) or [`is_superuser`](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.User.is_superuser). -This decorator can be used with or without parentheses. - === "components.py" ```python - from django_idom.decorators import auth_required - from django_idom.hooks import use_websocket - from idom import component, html - - @component - @auth_required - def my_component(): - return html.div("I am logged in!") + {% include "../../python/auth-required.py" %} ``` ??? example "See Interface" @@ -49,17 +44,7 @@ This decorator can be used with or without parentheses. === "components.py" ```python - from django_idom.decorators import auth_required - from idom import component, html - - @component - def my_component_fallback(): - return html.div("I am NOT logged in!") - - @component - @auth_required(fallback=my_component_fallback) - def my_component(): - return html.div("I am logged in!") + {% include "../../python/auth-required-component-fallback.py" %} ``` ??? question "How do I render a simple `idom.html` snippet if authentication fails?" @@ -69,14 +54,7 @@ This decorator can be used with or without parentheses. === "components.py" ```python - from django_idom.decorators import auth_required - from django_idom.hooks import use_websocket - from idom import component, html - - @component - @auth_required(fallback=html.div("I am NOT logged in!")) - def my_component(): - return html.div("I am logged in!") + {% include "../../python/auth-required-vdom-fallback.py" %} ``` ??? question "How can I check if a user `is_staff`?" @@ -86,15 +64,7 @@ This decorator can be used with or without parentheses. === "components.py" ```python - from django_idom.decorators import auth_required - from django_idom.hooks import use_websocket - from idom import component, html - - - @component - @auth_required(auth_attribute="is_staff") - def my_component(): - return html.div("I am logged in!") + {% include "../../python/auth-required-attribute.py" %} ``` ??? question "How can I check for a custom attribute?" @@ -106,12 +76,7 @@ This decorator can be used with or without parentheses. === "models.py" ```python - from django.contrib.auth.models import AbstractBaseUser - - class CustomUserModel(AbstractBaseUser): - @property - def is_really_cool(self): - return True + {% include "../../python/auth-required-custom-attribute-model.py" %} ``` ... then you would do the following within your decorator: @@ -119,12 +84,5 @@ This decorator can be used with or without parentheses. === "components.py" ```python - from django_idom.decorators import auth_required - from django_idom.hooks import use_websocket - from idom import component, html - - @component - @auth_required(auth_attribute="is_really_cool") - def my_component(): - return html.div("I am logged in!") + {% include "../../python/auth-required-custom-attribute.py" %} ``` diff --git a/docs/src/features/hooks.md b/docs/src/features/hooks.md index 07040d0b..23fcd6d8 100644 --- a/docs/src/features/hooks.md +++ b/docs/src/features/hooks.md @@ -1,12 +1,16 @@ -???+ summary +## Overview + +!!! summary Prefabricated hooks can be used within your `components.py` to help simplify development. -??? tip "Looking for standard ReactJS hooks?" +??? tip "Looking for standard React hooks?" + + Standard hooks are contained within [`idom-team/idom`](https://github.com/idom-team/idom). Since `idom` is installed alongside `django-idom`, you can import them at any time. - Standard ReactJS hooks are contained within [`idom-team/idom`](https://github.com/idom-team/idom). Since `idom` is installed by default alongside `django-idom`, you can import them at any time. + Check out the [IDOM Core docs](https://reactpy.dev/docs/reference/hooks-api.html#basic-hooks) to see what hooks are available! - Check out the [IDOM Core docs](https://idom-docs.herokuapp.com/docs/reference/hooks-api.html#basic-hooks) to see what hooks are available! +--- ## Use Query @@ -17,34 +21,13 @@ The function you provide into this hook must return either a `Model` or `QuerySe === "components.py" ```python - from example_project.my_app.models import TodoItem - from idom import component, html - from django_idom.hooks import use_query - - def get_items(): - return TodoItem.objects.all() - - @component - def todo_list(): - item_query = use_query(get_items) - - if item_query.loading: - rendered_items = html.h2("Loading...") - elif item_query.error: - rendered_items = html.h2("Error when loading!") - else: - rendered_items = html.ul(html.li(item, key=item) for item in item_query.data) - - return rendered_items + {% include "../../python/use-query.py" %} ``` === "models.py" ```python - from django.db import models - - class TodoItem(models.Model): - text = models.CharField(max_length=255) + {% include "../../python/example/models.py" %} ``` ??? example "See Interface" @@ -71,21 +54,7 @@ The function you provide into this hook must return either a `Model` or `QuerySe === "components.py" ```python - from idom import component - from django_idom.hooks import use_query - - def example_query(value: int, other_value: bool = False): - ... - - @component - def my_component(): - query = use_query( - example_query, - 123, - other_value=True, - ) - - ... + {% include "../../python/use-query-args.py" %} ``` ??? question "Why does the example `get_items` function return `TodoItem.objects.all()`?" @@ -108,25 +77,7 @@ The function you provide into this hook must return either a `Model` or `QuerySe === "components.py" ```python - from idom import component - from django_idom.types import QueryOptions - from django_idom.hooks import use_query - - def execute_io_intensive_operation(): - """This is an example query function that does something IO intensive.""" - pass - - @component - def todo_list(): - query = use_query( - QueryOptions(postprocessor=None), - execute_io_intensive_operation, - ) - - if query.loading or query.error: - return None - - return str(query.data) + {% include "../../python/use-query-postprocessor-disable.py" %} ``` If you wish to create a custom `postprocessor`, you will need to create a callable. @@ -138,34 +89,7 @@ The function you provide into this hook must return either a `Model` or `QuerySe === "components.py" ```python - from idom import component - from django_idom.types import QueryOptions - from django_idom.hooks import use_query - - def my_postprocessor(data, example_kwarg=True): - if example_kwarg: - return data - - return dict(data) - - def execute_io_intensive_operation(): - """This is an example query function that does something IO intensive.""" - pass - - @component - def todo_list(): - query = use_query( - QueryOptions( - postprocessor=my_postprocessor, - postprocessor_kwargs={"example_kwarg": False}, - ), - execute_io_intensive_operation, - ) - - if query.loading or query.error: - return None - - return str(query.data) + {% include "../../python/use-query-postprocessor-change.py" %} ``` ??? question "How can I prevent this hook from recursively fetching `ManyToMany` fields or `ForeignKey` relationships?" @@ -179,36 +103,10 @@ The function you provide into this hook must return either a `Model` or `QuerySe === "components.py" ```python - from example_project.my_app.models import MyModel - from idom import component - from django_idom.types import QueryOptions - from django_idom.hooks import use_query - - def get_model_with_relationships(): - """This is an example query function that gets `MyModel` which has a ManyToMany field, and - additionally other models that have formed a ForeignKey association to `MyModel`. - - ManyToMany Field: `many_to_many_field` - ForeignKey Field: `foreign_key_field_set` - """ - return MyModel.objects.get(id=1) - - @component - def todo_list(): - query = use_query( - QueryOptions(postprocessor_kwargs={"many_to_many": False, "many_to_one": False}), - get_model_with_relationships, - ) - - if query.loading or query.error: - return None - - # By disabling `many_to_many` and `many_to_one`, accessing these fields will now - # generate a `SynchronousOnlyOperation` exception - return f"{query.data.many_to_many_field} {query.data.foriegn_key_field_set}" + {% include "../../python/use-query-postprocessor-kwargs.py" %} ``` - _Note: In Django's ORM design, the field name to access foreign keys is [always be postfixed with `_set`](https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_one/)._ + _Note: In Django's ORM design, the field name to access foreign keys is [postfixed with `_set`](https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_one/) by default._ ??? question "Can I make ORM calls without hooks?" @@ -223,37 +121,14 @@ The function you provide into this hook will have no return value. === "components.py" ```python - from example_project.my_app.models import TodoItem - from idom import component, html - from django_idom.hooks import use_mutation - - def add_item(text: str): - TodoItem(text=text).save() - - @component - def todo_list(): - def submit_event(event): - if event["key"] == "Enter": - item_mutation.execute(text=event["target"]["value"]) - - item_mutation = use_mutation(add_item) - if item_mutation.loading: - mutation_status = html.h2("Adding...") - elif item_mutation.error: - mutation_status = html.h2("Error when adding!") - else: - mutation_status = "" - - return html.div( - html.label("Add an item:"), - html.input({"type": "text", "onKeyDown": submit_event}), - mutation_status, - ) + {% include "../../python/use-mutation.py" %} ``` === "models.py" - {% include-markdown "../../includes/examples.md" start="" end="" %} + ```python + {% include "../../python/example/models.py" %} + ``` ??? example "See Interface" @@ -277,19 +152,7 @@ The function you provide into this hook will have no return value. === "components.py" ```python - from idom import component - from django_idom.hooks import use_mutation - - def example_mutation(value: int, other_value: bool = False): - ... - - @component - def my_component(): - mutation = use_mutation(example_mutation) - - mutation.execute(123, other_value=True) - - ... + {% include "../../python/use-mutation-args-kwargs.py" %} ``` ??? question "Can `use_mutation` trigger a refetch of `use_query`?" @@ -303,52 +166,14 @@ The function you provide into this hook will have no return value. === "components.py" ```python - from example_project.my_app.models import TodoItem - from idom import component, html - from django_idom.hooks import use_mutation - - def get_items(): - return TodoItem.objects.all() - - def add_item(text: str): - TodoItem(text=text).save() - - @component - def todo_list(): - item_query = use_query(get_items) - item_mutation = use_mutation(add_item, refetch=get_items) - - def submit_event(event): - if event["key"] == "Enter": - item_mutation.execute(text=event["target"]["value"]) - - # Handle all possible query states - if item_query.loading: - rendered_items = html.h2("Loading...") - elif item_query.error: - rendered_items = html.h2("Error when loading!") - else: - rendered_items = html.ul(html.li(item, key=item) for item in item_query.data) - - # Handle all possible mutation states - if item_mutation.loading: - mutation_status = html.h2("Adding...") - elif item_mutation.error: - mutation_status = html.h2("Error when adding!") - else: - mutation_status = "" - - return html.div( - html.label("Add an item:"), - html.input({"type": "text", "onKeyDown": submit_event}), - mutation_status, - rendered_items, - ) + {% include "../../python/use-mutation-query-refetch.py" %} ``` === "models.py" - {% include-markdown "../../includes/examples.md" start="" end="" %} + ```python + {% include "../../python/example/models.py" %} + ``` ??? question "Can I make a failed `use_mutation` try again?" @@ -359,60 +184,27 @@ The function you provide into this hook will have no return value. === "components.py" ```python - from example_project.my_app.models import TodoItem - from idom import component, html - from django_idom.hooks import use_mutation - - def add_item(text: str): - TodoItem(text=text).save() - - @component - def todo_list(): - item_mutation = use_mutation(add_item) - - def reset_event(event): - item_mutation.reset() - - def submit_event(event): - if event["key"] == "Enter": - item_mutation.execute(text=event["target"]["value"]) - - if item_mutation.loading: - mutation_status = html.h2("Adding...") - elif item_mutation.error: - mutation_status = html.button({"onClick": reset_event}, "Error: Try again!") - else: - mutation_status = "" - - return html.div( - html.label("Add an item:"), - html.input({"type": "text", "onKeyDown": submit_event}), - mutation_status, - ) + {% include "../../python/use-mutation-reset.py" %} ``` === "models.py" - {% include-markdown "../../includes/examples.md" start="" end="" %} + ```python + {% include "../../python/example/models.py" %} + ``` ??? question "Can I make ORM calls without hooks?" {% include-markdown "../../includes/orm.md" start="" end="" %} -## Use Websocket +## Use Connection -You can fetch the Django Channels [websocket](https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer) at any time by using `use_websocket`. +You can fetch the Django Channels [websocket](https://channels.readthedocs.io/en/stable/topics/consumers.html#asyncjsonwebsocketconsumer) at any time by using `use_connection`. === "components.py" ```python - from idom import component, html - from django_idom.hooks import use_websocket - - @component - def my_component(): - my_websocket = use_websocket() - return html.div(my_websocket) + {% include "../../python/use-connection.py" %} ``` ??? example "See Interface" @@ -425,7 +217,7 @@ You can fetch the Django Channels [websocket](https://channels.readthedocs.io/en | Type | Description | | --- | --- | - | `IdomWebsocket` | The component's websocket. | + | `Connection` | The component's websocket. | ## Use Scope @@ -434,13 +226,7 @@ This is a shortcut that returns the Websocket's [`scope`](https://channels.readt === "components.py" ```python - from idom import component, html - from django_idom.hooks import use_scope - - @component - def my_component(): - my_scope = use_scope() - return html.div(my_scope) + {% include "../../python/use-scope.py" %} ``` ??? example "See Interface" @@ -453,7 +239,7 @@ This is a shortcut that returns the Websocket's [`scope`](https://channels.readt | Type | Description | | --- | --- | - | `dict[str, Any]` | The websocket's `scope`. | + | `MutableMapping[str, Any]` | The websocket's `scope`. | ## Use Location @@ -464,13 +250,7 @@ You can expect this hook to provide strings such as `/idom/my_path`. === "components.py" ```python - from idom import component, html - from django_idom.hooks import use_location - - @component - def my_component(): - my_location = use_location() - return html.div(my_location) + {% include "../../python/use-location.py" %} ``` ??? example "See Interface" @@ -500,13 +280,7 @@ You can expect this hook to provide strings such as `http://example.com`. === "components.py" ```python - from idom import component, html - from django_idom.hooks import use_origin - - @component - def my_component(): - my_origin = use_origin() - return html.div(my_origin) + {% include "../../python/use-origin.py" %} ``` ??? example "See Interface" diff --git a/docs/src/features/settings.md b/docs/src/features/settings.md index 4eb83fe1..711c267d 100644 --- a/docs/src/features/settings.md +++ b/docs/src/features/settings.md @@ -1,32 +1,21 @@ -???+ summary +## Overview - Django-IDOM uses your **Django project's** `settings.py` file to modify the behavior of IDOM. +!!! summary -## Primary Configuration - -=== "settings.py" + Your **Django project's** `settings.py` can modify the behavior of IDOM. - +--- - ```python - # If "idom" cache is not configured, then we will use "default" instead - CACHES = { - "idom": {"BACKEND": ...}, - } +## Primary Configuration - # Maximum seconds between two reconnection attempts that would cause the client give up. - # 0 will disable reconnection. - IDOM_WS_MAX_RECONNECT_TIMEOUT = 604800 +These are Django-IDOM's default settings values. You can modify these values in your **Django project's** `settings.py` to change the behavior of IDOM. - # The URL for IDOM to serve websockets - IDOM_WEBSOCKET_URL = "idom/" +=== "settings.py" - # Dotted path to the default postprocessor function, or `None` - IDOM_DEFAULT_QUERY_POSTPROCESSOR = "example_project.utils.my_postprocessor" + ```python + {% include "../../python/settings.py" %} ``` - - ??? question "Do I need to modify my settings?" The default configuration of IDOM is adequate for the majority of use cases. diff --git a/docs/src/features/templatetag.md b/docs/src/features/template-tag.md similarity index 51% rename from docs/src/features/templatetag.md rename to docs/src/features/template-tag.md index 6aa58023..7d5d2c10 100644 --- a/docs/src/features/templatetag.md +++ b/docs/src/features/template-tag.md @@ -1,50 +1,70 @@ -???+ summary +## Overview + +!!! summary Template tags can be used within your Django templates such as `my-template.html` to import IDOM features. +--- + ## Component +The `component` template tag can be used to insert any number of IDOM components onto your page. + === "my-template.html" {% include-markdown "../../../README.md" start="" end="" %} +??? example "See Interface" + + **Parameters** + + | Name | Type | Description | Default | + | --- | --- | --- | --- | + | `dotted_path` | `str` | The dotted path to the component to render. | N/A | + | `*args` | `Any` | The positional arguments to provide to the component. | N/A | + | `**kwargs` | `Any` | The keyword arguments to provide to the component. | N/A | + + **Returns** + + | Type | Description | + | --- | --- | + | `Component` | An IDOM component. | + ??? warning "Do not use context variables for the IDOM component name" - Our pre-processor relies on the template tag containing a string. + Our preprocessor relies on the template tag containing a string. - **Do not** use Django template/context variables for the component path. Failure to follow this warning will result in unexpected behavior. + **Do not** use Django template/context variables for the component path. Failure to follow this warning can result in unexpected behavior. For example, **do not** do the following: === "my-template.html" ```jinja - - {% component dont_do_this recipient="World" %} - {% component "example_project.my_app.components.hello_world" recipient="World" %} + + + {% component dont_do_this recipient="World" %} ``` === "views.py" ```python - def example_view(): - context_vars = {"dont_do_this": "example_project.my_app.components.hello_world"} - return render(request, "my-template.html", context_vars) + {% include "../../python/template-tag-bad-view.py" %} ``` - + ??? info "Reserved keyword arguments: `class` and `key`" For this template tag, there are two reserved keyword arguments: `class` and `key` - `class` allows you to apply a HTML class to the top-level component div. This is useful for styling purposes. - - `key` allows you to force the component to use a [specific key value](https://idom-docs.herokuapp.com/docs/guides/understanding-idom/why-idom-needs-keys.html?highlight=key). You typically won't need to set this. + - `key` allows you to force the component's root node to use a [specific key value](https://reactpy.dev/docs/guides/creating-interfaces/rendering-data/index.html#organizing-items-with-keys). Using `key` within a template tag is effectively useless. === "my-template.html" @@ -54,23 +74,7 @@ ... ``` - - -??? example "See Interface" - - **Parameters** - - | Name | Type | Description | Default | - | --- | --- | --- | --- | - | `dotted_path` | `str` | The dotted path to the component to render. | N/A | - | `**kwargs` | `Any` | The keyword arguments to pass to the component. | N/A | - - **Returns** - - | Type | Description | - | --- | --- | - | `Component` | An IDOM component. | - + ??? question "Can I use multiple components on one page?" @@ -84,35 +88,35 @@ - {% component "example_project.my_app.components.hello_world" recipient="World" %} - {% component "example_project.my_app_2.components.class_component" class="bold small-font" %} -
{% component "example_project.my_app_3.components.simple_component" %}
+

{% component "example_project.my_app.components.my_title" %}

+

{% component "example_project.my_app_2.components.goodbye_world" class="bold small-font" %}

+ {% component "example_project.my_app_3.components.simple_button" %} ``` - But keep in mind, in scenarios where you are trying to create a Single Page Application (SPA) within Django, you will only have one central component within your `#!html ` tag. + Please note that components separated like this will not be able to interact with each other, except through database queries. - Additionally, the components in the example above will not be able to interact with each other, except through database queries. + Additionally, in scenarios where you are trying to create a Single Page Application (SPA) within Django, you will only have one component within your `#!html ` tag. - - -??? question "Can I use positional arguments instead of keyword arguments?" - You can only pass in **keyword arguments** within the template tag. Due to technical limitations, **positional arguments** are not supported at this time. + - - +??? question "Can I use positional arguments instead of keyword arguments?" -??? question "What is a "template tag"?" + You can use any combination of `*args`/`**kwargs` in your template tag. - You can think of template tags as Django's way of allowing you to run Python code within your HTML. Django-IDOM uses a `#!jinja {% component ... %}` template tag to perform it's magic. + === "my-template.html" - Keep in mind, in order to use the `#!jinja {% component ... %}` tag, you will need to first call `#!jinja {% load idom %}` to gain access to it. + ```jinja + {% component "example_project.my_app.components.frog_greeter" 123 "Mr. Froggles" species="Grey Treefrog" %} + ``` - === "my-template.html" + === "components.py" - {% include-markdown "../../../README.md" start="" end="" %} + ```python + {% include "../../python/template-tag-args-kwargs.py" %} + ``` - + diff --git a/docs/src/features/utils.md b/docs/src/features/utils.md new file mode 100644 index 00000000..73c5248d --- /dev/null +++ b/docs/src/features/utils.md @@ -0,0 +1,41 @@ +## Overview + +!!! summary + + Utility functions that you can use when needed. + +--- + +## Django Query Postprocessor + +This is the default postprocessor for the `use_query` hook. + +This postprocessor is designed to avoid Django's `SynchronousOnlyException` by recursively fetching all fields within a `Model` or `QuerySet` to prevent [lazy execution](https://docs.djangoproject.com/en/dev/topics/db/queries/#querysets-are-lazy). + +=== "components.py" + + ```python + {% include "../../python/django-query-postprocessor.py" %} + ``` + +=== "models.py" + + ```python + {% include "../../python/example/models.py" %} + ``` + +??? example "See Interface" + + **Parameters** + + | Name | Type | Description | Default | + | --- | --- | --- | --- | + | `data` | `QuerySet | Model` | The `Model` or `QuerySet` to recursively fetch fields from. | N/A | + | `many_to_many` | `bool` | Whether or not to recursively fetch `ManyToManyField` relationships. | `True` | + | `many_to_one` | `bool` | Whether or not to recursively fetch `ForeignKey` relationships. | `True` | + + **Returns** + + | Type | Description | + | --- | --- | + | `QuerySet | Model` | The `Model` or `QuerySet` with all fields fetched. | diff --git a/docs/src/getting-started/choose-django-app.md b/docs/src/get-started/choose-django-app.md similarity index 84% rename from docs/src/getting-started/choose-django-app.md rename to docs/src/get-started/choose-django-app.md index b414cc2a..fffb8c76 100644 --- a/docs/src/getting-started/choose-django-app.md +++ b/docs/src/get-started/choose-django-app.md @@ -1,10 +1,14 @@ -???+ summary +## Overview + +!!! summary Set up a **Django Project** with at least one app. --- -If you have reached this point, you should have already [installed Django-IDOM](../getting-started/installation.md) through the previous steps. +## Choose a Django App + +If you have reached this point, you should have already [installed Django-IDOM](../get-started/installation.md) through the previous steps. You will now need to pick at least one **Django app** to start using Django-IDOM on. diff --git a/docs/src/getting-started/create-component.md b/docs/src/get-started/create-component.md similarity index 94% rename from docs/src/getting-started/create-component.md rename to docs/src/get-started/create-component.md index e024a3e9..8a74350d 100644 --- a/docs/src/getting-started/create-component.md +++ b/docs/src/get-started/create-component.md @@ -1,9 +1,13 @@ -???+ summary +## Overview + +!!! summary Create a component function using our decorator. --- +## Create a Component + {% include-markdown "../../../README.md" start="" end="" %} === "components.py" diff --git a/docs/src/getting-started/installation.md b/docs/src/get-started/installation.md similarity index 57% rename from docs/src/getting-started/installation.md rename to docs/src/get-started/installation.md index 1224df32..f822c8b2 100644 --- a/docs/src/getting-started/installation.md +++ b/docs/src/get-started/installation.md @@ -1,14 +1,18 @@ -???+ summary +## Overview + +!!! summary Django-IDOM can be installed from PyPI to an existing **Django project** with minimal configuration. -## Step 0: Set up a Django Project +--- + +## Step 0: Create a Django Project These docs assumes you have already created [a **Django project**](https://docs.djangoproject.com/en/dev/intro/tutorial01/), which involves creating and installing at least one **Django app**. If not, check out this [9 minute YouTube tutorial](https://www.youtube.com/watch?v=ZsJRXS_vrw0) created by _IDG TECHtalk_. ## Step 1: Install from PyPI -```bash +```bash linenums="0" pip install django-idom ``` @@ -19,15 +23,12 @@ In your settings you will need to add `django_idom` to [`INSTALLED_APPS`](https: === "settings.py" ```python - INSTALLED_APPS = [ - "django_idom", - ... - ] + {% include "../../python/configure-installed-apps.py" %} ``` -??? warning "Enable Django ASGI (Required)" +??? warning "Enable Django Channels ASGI (Required)" - Django-IDOM requires ASGI in order to use Websockets. + Django-IDOM requires ASGI Websockets from [Django Channels](https://github.com/django/channels). If you have not enabled ASGI on your **Django project** yet, you will need to install `channels[daphne]`, add `daphne` to `INSTALLED_APPS`, then set your `ASGI_APPLICATION` variable. @@ -36,18 +37,16 @@ In your settings you will need to add `django_idom` to [`INSTALLED_APPS`](https: === "settings.py" ```python - INSTALLED_APPS = [ - "daphne", - ... - ] - ASGI_APPLICATION = "example_project.asgi.application" + {% include "../../python/configure-channels.py" %} ``` ??? note "Configure IDOM settings (Optional)" Below are a handful of values you can change within `settings.py` to modify the behavior of IDOM. - {% include-markdown "../features/settings.md" start="" end="" preserve-includer-indent=false %} + ```python + {% include "../../python/settings.py" %} + ``` ## Step 3: Configure [`urls.py`](https://docs.djangoproject.com/en/dev/topics/http/urls/) @@ -56,12 +55,7 @@ Add IDOM HTTP paths to your `urlpatterns`. === "urls.py" ```python - from django.urls import include, path - - urlpatterns = [ - path("idom/", include("django_idom.http.urls")), - ... - ] + {% include "../../python/configure-urls.py" %} ``` ## Step 4: Configure [`asgi.py`](https://docs.djangoproject.com/en/dev/howto/deployment/asgi/) @@ -71,28 +65,17 @@ Register IDOM's Websocket using `IDOM_WEBSOCKET_PATH`. === "asgi.py" ```python - import os - from django.core.asgi import get_asgi_application - - # Ensure DJANGO_SETTINGS_MODULE is set properly based on your project name! - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_project.settings") - django_asgi_app = get_asgi_application() - - from channels.auth import AuthMiddlewareStack - from channels.routing import ProtocolTypeRouter, URLRouter - from channels.sessions import SessionMiddlewareStack - from django_idom import IDOM_WEBSOCKET_PATH - - application = ProtocolTypeRouter( - { - "http": django_asgi_app, - "websocket": SessionMiddlewareStack( - AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH])) - ), - } - ) + {% include "../../python/configure-asgi.py" %} ``` ??? question "Where is my `asgi.py`?" If you do not have an `asgi.py`, follow the [`channels` installation guide](https://channels.readthedocs.io/en/stable/installation.html). + +## Step 5: Run Migrations + +Run Django's database migrations to initialize Django-IDOM's database table. + +```bash linenums="0" +python manage.py migrate +``` diff --git a/docs/src/get-started/learn-more.md b/docs/src/get-started/learn-more.md new file mode 100644 index 00000000..7ffd8040 --- /dev/null +++ b/docs/src/get-started/learn-more.md @@ -0,0 +1,11 @@ +# :confetti_ball: Congratulations :confetti_ball: + +If you followed the previous steps, you have now created a "Hello World" component! + +The docs you are reading only covers our Django integration. To learn more about features, such as interactive events and hooks, check out the [IDOM Core Documentation](https://reactpy.dev/docs/guides/creating-interfaces/index.html)! + +Additionally, the vast majority of tutorials/guides you find for ReactJS can be applied to IDOM. + +=== "Learn More" + + [Django-IDOM Advanced Usage](../features/components.md){ .md-button .md-button--primary} [IDOM Core Documentation](https://reactpy.dev/docs/guides/creating-interfaces/index.html){ .md-button .md-button--primary } [Ask Questions](https://github.com/idom-team/idom/discussions){ .md-button .md-button--primary } diff --git a/docs/src/getting-started/render-view.md b/docs/src/get-started/render-view.md similarity index 84% rename from docs/src/getting-started/render-view.md rename to docs/src/get-started/render-view.md index 6098680b..30e0ead6 100644 --- a/docs/src/getting-started/render-view.md +++ b/docs/src/get-started/render-view.md @@ -1,9 +1,13 @@ -???+ summary +## Overview + +!!! summary Select your template containing an IDOM component, and render it using a Django view. --- +## Render Your View + We will assume you have [created a Django View](https://docs.djangoproject.com/en/dev/intro/tutorial01/#write-your-first-view) before, but here's a simple example below. Within your **Django app**'s `views.py` file, you will need to create a function to render the HTML template containing your IDOM components. @@ -13,10 +17,7 @@ In this example, we will create a view that renders `my-template.html` (_from th === "views.py" ```python - from django.shortcuts import render - - def index(request): - return render(request, "my-template.html") + {% include "../../python/example/views.py" %} ``` We will add this new view into your [`urls.py`](https://docs.djangoproject.com/en/dev/intro/tutorial01/#write-your-first-view). @@ -24,12 +25,7 @@ We will add this new view into your [`urls.py`](https://docs.djangoproject.com/e === "urls.py" ```python - from django.urls import path - from example_project.my_app import views - - urlpatterns = [ - path("example/", views.index), - ] + {% include "../../python/example/urls.py" %} ``` Now, navigate to `http://127.0.0.1:8000/example/`. If you copy-pasted the component from the previous example, you will now see your component display "Hello World". diff --git a/docs/src/getting-started/reference-component.md b/docs/src/get-started/use-template-tag.md similarity index 56% rename from docs/src/getting-started/reference-component.md rename to docs/src/get-started/use-template-tag.md index f7c03ecb..6fd9017c 100644 --- a/docs/src/getting-started/reference-component.md +++ b/docs/src/get-started/use-template-tag.md @@ -1,24 +1,24 @@ -???+ summary +## Overview + +!!! summary Decide where the component will be displayed by using our template tag. --- +## Use the Template Tag + {% include-markdown "../../../README.md" start="" end="" %} === "my-template.html" {% include-markdown "../../../README.md" start="" end="" %} -{% include-markdown "../features/templatetag.md" start="" end="" %} - -{% include-markdown "../features/templatetag.md" start="" end="" %} - -{% include-markdown "../features/templatetag.md" start="" end="" %} +{% include-markdown "../features/template-tag.md" start="" end="" %} -{% include-markdown "../features/templatetag.md" start="" end="" %} +{% include-markdown "../features/template-tag.md" start="" end="" %} -{% include-markdown "../features/templatetag.md" start="" end="" %} +{% include-markdown "../features/template-tag.md" start="" end="" %} ??? question "Where is my templates folder?" diff --git a/docs/src/getting-started/learn-more.md b/docs/src/getting-started/learn-more.md deleted file mode 100644 index b41b6484..00000000 --- a/docs/src/getting-started/learn-more.md +++ /dev/null @@ -1,11 +0,0 @@ -# :confetti_ball: Congratulations :confetti_ball: - -If you followed the previous steps, you have now created a "Hello World" component! - -The docs you are reading only covers our Django integration. To learn more about features, such as interactive events and hooks, check out the [IDOM Core Documentation](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/index.html)! - -Additionally, the vast majority of tutorials/guides you find for React can be applied to IDOM. - -| Learn More | -| --- | -| [Django-IDOM Advanced Usage](../features/components.md){ .md-button } [IDOM Core Documentation](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/index.html){ .md-button } [Ask Questions on GitHub Discussions](https://github.com/idom-team/idom/discussions){ .md-button .md-button--primary } | diff --git a/docs/src/stylesheets/extra.css b/docs/src/stylesheets/extra.css index 259625f9..72e8bd26 100644 --- a/docs/src/stylesheets/extra.css +++ b/docs/src/stylesheets/extra.css @@ -1,12 +1,4 @@ -.md-footer__inner { - display: none; -} - -.md-tabs, -.md-header { - background-color: var(--md-footer-bg-color--dark); -} - +/* Reduce the insane amounts of white space the default theme has */ .md-typeset :is(.admonition, details) { margin: 0.55em 0; } @@ -16,14 +8,230 @@ padding-bottom: 0.35em; } -body[data-md-color-scheme="slate"] - .md-typeset - :is(.admonition, details):is(.question, .help, .faq) { - border-color: #366517; +/* Font size for admonitions */ +.md-typeset .admonition.summary, +.md-typeset details.summary { + font-size: 0.7rem; } -body[data-md-color-scheme="slate"] +/* Colors for admonitions */ +[data-md-color-scheme="slate"] .md-typeset - :-webkit-any(.admonition, details):-webkit-any(.question, .help, .faq) { - border-color: #366517; + details:not(.warning, .failure, .danger, .bug) + > .admonition-title, +[data-md-color-scheme="slate"] + .md-typeset + details:not(.warning, .failure, .danger, .bug) + > summary { + background: var(--md-primary-fg-color) !important; +} + +[data-md-color-scheme="slate"] .md-typeset .admonition, +[data-md-color-scheme="slate"] .md-typeset details { + border-color: transparent !important; +} + +[data-md-color-scheme="slate"] .md-typeset .admonition.summary, +[data-md-color-scheme="slate"] .md-typeset details.summary { + background: #353a45; +} + +[data-md-color-scheme="slate"] .md-typeset details > .admonition-title:after, +[data-md-color-scheme="slate"] .md-typeset details > summary:after { + color: var(--md-admonition-fg-color) !important; +} + +/* Colors for summary admonition */ +[data-md-color-scheme="slate"] .md-typeset .admonition.summary, +[data-md-color-scheme="slate"] .md-typeset details.summary { + background: #353a45; +} + +[data-md-color-scheme="slate"] .md-typeset details.summary > .admonition-title, +[data-md-color-scheme="slate"] .md-typeset details.summary > summary { + background: #353a45 !important; +} + +[data-md-color-scheme="slate"] .md-typeset .summary .admonition-title, +[data-md-color-scheme="slate"] .md-typeset .summary summary { + background: transparent; +} + +/* Move the sidebars to the edges of the page */ +.md-main__inner.md-grid { + margin-left: 0; + margin-right: 0; + max-width: unset; + display: flex; + justify-content: center; +} + +.md-sidebar--primary { + margin-right: auto; +} + +.md-sidebar.md-sidebar--secondary { + margin-left: auto; +} + +.md-content { + max-width: 56rem; +} + +/* Maintain content positioning even if sidebars are disabled */ +@media screen and (min-width: 76.1875em) { + .md-sidebar { + display: block; + } + + .md-sidebar[hidden] { + visibility: hidden; + } +} + +/* Sidebar styling */ +@media screen and (min-width: 76.1875em) { + .md-nav__title[for="__toc"] { + text-transform: uppercase; + margin: 0.5rem; + } + + .md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link { + color: rgb(133 142 159); + margin: 0.5rem; + } + + .md-nav__item .md-nav__link { + position: relative; + } + + .md-nav__link:is(:focus, :hover):not(.md-nav__link--active) { + color: unset; + } + + .md-nav__item + .md-nav__link:is(:focus, :hover):not(.md-nav__link--active):before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.2; + z-index: -1; + background-color: grey; + } + + .md-nav__item .md-nav__link--active:before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.15; + z-index: -1; + background-color: var(--md-typeset-a-color); + } + + .md-nav__link { + padding: 0.5rem 0.5rem 0.5rem 1rem; + margin: 0; + border-radius: 0 10px 10px 0; + font-weight: 500; + } + + .md-sidebar__scrollwrap { + margin: 0; + } + + [dir="ltr"] + .md-nav--lifted + .md-nav[data-md-level="1"] + > .md-nav__list + > .md-nav__item { + padding: 0; + } + + .md-nav__item--nested .md-nav__item .md-nav__item { + padding: 0; + } + + .md-nav__item--nested .md-nav__item .md-nav__item .md-nav__link { + font-weight: 400; + padding-left: 1.5rem; + } +} + +/* Table of Contents styling */ +@media screen and (min-width: 60em) { + [data-md-component="sidebar"] .md-nav__title[for="__toc"] { + text-transform: uppercase; + margin: 0.5rem; + margin-left: 0; + } + + [data-md-component="toc"] .md-nav__item .md-nav__link--active { + position: relative; + } + + [data-md-component="toc"] .md-nav__item .md-nav__link--active:before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.15; + z-index: -1; + background-color: var(--md-typeset-a-color); + } + + [data-md-component="toc"] .md-nav__link { + padding: 0.5rem 0.5rem; + margin: 0; + border-radius: 10px 0 0 10px; + } + [dir="ltr"] .md-sidebar__inner { + padding: 0; + } + + .md-nav__item { + padding: 0; + } +} + +/* Page background color */ +[data-md-color-scheme="slate"] { + --md-hue: 225; + --md-default-bg-color: hsla(var(--md-hue), 15%, 16%, 1); + --md-default-bg-color--light: hsla(var(--md-hue), 15%, 16%, 0.54); + --md-default-bg-color--lighter: hsla(var(--md-hue), 15%, 16%, 0.26); + --md-default-bg-color--lightest: hsla(var(--md-hue), 15%, 16%, 0.07); + --md-code-bg-color: #16181d; + --md-primary-fg-color: #2b3540; + --md-default-fg-color--light: #fff; + --md-typeset-a-color: #00b0f0; + --md-code-hl-comment-color: hsla(var(--md-hue), 75%, 90%, 0.43); +} + +/* Font changes */ +.md-typeset h1 { + font-weight: 500; +} + +.md-typeset h1:not([id]) { + display: none; +} + +.md-typeset h2 { + font-weight: 400; +} + +/* Hide invisible jump selectors */ +h2#overview { + visibility: hidden; + height: 0; + margin: 0; + padding: 0; } diff --git a/mkdocs.yml b/mkdocs.yml index f51bc051..78fb08af 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,35 +1,39 @@ --- nav: - Home: index.md - - Getting Started: - - 1. Install Django-IDOM: getting-started/installation.md - - 2. Choose a Django App: getting-started/choose-django-app.md - - 3. Create a Component: getting-started/create-component.md - - 4. Use the Template Tag: getting-started/reference-component.md - - 5. Render Your View: getting-started/render-view.md - - 6. Learn More: getting-started/learn-more.md - - Usage: + - Get Started: + - Install Django-IDOM: get-started/installation.md + - Choose a Django App: get-started/choose-django-app.md + - Create a Component: get-started/create-component.md + - Use the Template Tag: get-started/use-template-tag.md + - Render Your View: get-started/render-view.md + - Learn More: get-started/learn-more.md + - Reference: - Components: features/components.md - Hooks: features/hooks.md - Decorators: features/decorators.md - - Template Tag: features/templatetag.md + - Utilities: features/utils.md + - Template Tag: features/template-tag.md - Settings: features/settings.md - - Contribute: - - Code: contribute/code.md - - Docs: contribute/docs.md - - Running Tests: contribute/running-tests.md - - Changelog: changelog/index.md + - About: + - Contribute: + - Code: contribute/code.md + - Docs: contribute/docs.md + - Running Tests: contribute/running-tests.md + - Community: https://github.com/idom-team/idom/discussions + - Changelog: changelog/index.md theme: name: material + custom_dir: docs/overrides palette: - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/white-balance-sunny name: Switch to light mode - primary: deep-orange - accent: orange + primary: light blue + accent: light blue - media: "(prefers-color-scheme: light)" scheme: default toggle: @@ -38,10 +42,9 @@ theme: primary: black features: - navigation.instant - - navigation.tracking - navigation.tabs - - toc.integrate - navigation.top + - content.code.copy icon: repo: fontawesome/brands/github @@ -63,7 +66,13 @@ markdown_extensions: plugins: - search + - git-authors - include-markdown + - minify: + minify_html: true + minify_js: true + minify_css: true + cache_safe: true - git-revision-date-localized: fallback_to_build_date: true - spellcheck: @@ -83,7 +92,7 @@ watch: - README.md - CHANGELOG.md -site_name: Django IDOM Docs +site_name: Django-IDOM Docs site_author: Archmonger site_description: React for Django developers. copyright: Copyright © 2022 IDOM Team diff --git a/pyproject.toml b/pyproject.toml index 6bf3a9fc..c9321d8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,15 @@ ensure_newline_before_comments = "True" include_trailing_comma = "True" line_length = 88 lines_after_imports = 2 +extend_skip_glob = ["*/migrations/*", '.nox/*', '.venv/*', 'build/*'] [tool.mypy] +exclude = [ + 'migrations/.*', +] ignore_missing_imports = true warn_unused_configs = true warn_redundant_casts = true warn_unused_ignores = true check_untyped_defs = true +incremental = false diff --git a/requirements.txt b/requirements.txt index 7328d474..63e3d68e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ --r requirements/pkg-deps.txt +-r requirements/build-docs.txt +-r requirements/build-pkg.txt -r requirements/check-style.txt +-r requirements/check-types.txt +-r requirements/dev-env.txt +-r requirements/pkg-deps.txt -r requirements/test-env.txt -r requirements/test-run.txt --r requirements/dev-env.txt diff --git a/requirements/build-docs.txt b/requirements/build-docs.txt index 304cc367..9ae8fcf1 100644 --- a/requirements/build-docs.txt +++ b/requirements/build-docs.txt @@ -4,3 +4,5 @@ mkdocs-material mkdocs-include-markdown-plugin linkcheckmd mkdocs-spellcheck[all] +mkdocs-git-authors-plugin +mkdocs-minify-plugin diff --git a/requirements/pkg-deps.txt b/requirements/pkg-deps.txt index d2329eb6..0413c4c1 100644 --- a/requirements/pkg-deps.txt +++ b/requirements/pkg-deps.txt @@ -1,4 +1,5 @@ channels >=4.0.0 -idom >=0.40.2, <0.41.0 +idom >=1.0.0, <1.1.0 aiofile >=3.0 +dill >=0.3.5 typing_extensions diff --git a/setup.py b/setup.py index 4d796537..9fd8e902 100644 --- a/setup.py +++ b/setup.py @@ -84,7 +84,7 @@ def list2cmdline(cmd_list): package["version"] = eval(line.split("=", 1)[1]) break else: - print("No version found in %s/__init__.py" % package_dir) + print(f"No version found in {package_dir}/__init__.py") sys.exit(1) @@ -95,9 +95,7 @@ def list2cmdline(cmd_list): requirements = [] with (root_dir / "requirements" / "pkg-deps.txt").open() as f: - for line in map(str.strip, f): - if not line.startswith("#"): - requirements.append(line) + requirements.extend(line for line in map(str.strip, f) if not line.startswith("#")) package["install_requires"] = requirements @@ -121,22 +119,42 @@ def list2cmdline(cmd_list): def build_javascript_first(cls): class Command(cls): def run(self): + js_dir = str(src_dir / "js") + npm = shutil.which("npm") # this is required on windows + if npm is None: + raise RuntimeError("NPM is not installed.") + + log.info("Updating NPM...") + try: + args_list = [npm, "install", "-g", "npm@latest"] + log.info(f"> {list2cmdline(args_list)}") + subprocess.run(args_list, cwd=js_dir, check=True) + except Exception: + log.error("Failed to update NPM") + log.error(traceback.format_exc()) + raise + log.info("Installing Javascript...") try: - js_dir = str(src_dir / "js") - npm = shutil.which("npm") # this is required on windows - if npm is None: - raise RuntimeError("NPM is not installed.") - for args in (f"{npm} install", f"{npm} run build"): - args_list = args.split() - log.info(f"> {list2cmdline(args_list)}") - subprocess.run(args_list, cwd=js_dir, check=True) + args_list = [npm, "install"] + log.info(f"> {list2cmdline(args_list)}") + subprocess.run(args_list, cwd=js_dir, check=True) except Exception: log.error("Failed to install Javascript") log.error(traceback.format_exc()) raise - else: - log.info("Successfully installed Javascript") + + log.info("Building Javascript...") + try: + args_list = [npm, "run", "build"] + log.info(f"> {list2cmdline(args_list)}") + subprocess.run(args_list, cwd=js_dir, check=True) + except Exception: + log.error("Failed to build Javascript") + log.error(traceback.format_exc()) + raise + + log.info("Successfully built Javascript") super().run() return Command diff --git a/src/django_idom/__init__.py b/src/django_idom/__init__.py index c1991661..6ab766f9 100644 --- a/src/django_idom/__init__.py +++ b/src/django_idom/__init__.py @@ -1,12 +1,10 @@ from django_idom import components, decorators, hooks, types, utils -from django_idom.types import IdomWebsocket from django_idom.websocket.paths import IDOM_WEBSOCKET_PATH -__version__ = "2.2.1" +__version__ = "3.0.0" __all__ = [ "IDOM_WEBSOCKET_PATH", - "IdomWebsocket", "hooks", "components", "decorators", diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py index 6a963664..bde9fb98 100644 --- a/src/django_idom/apps.py +++ b/src/django_idom/apps.py @@ -1,6 +1,12 @@ +import logging + from django.apps import AppConfig +from django.db.utils import DatabaseError + +from django_idom.utils import ComponentPreloader, db_cleanup -from django_idom.utils import ComponentPreloader + +_logger = logging.getLogger(__name__) class DjangoIdomConfig(AppConfig): @@ -8,4 +14,15 @@ class DjangoIdomConfig(AppConfig): def ready(self): # Populate the IDOM component registry when Django is ready - ComponentPreloader().register_all() + ComponentPreloader().run() + + # Delete expired database entries + # Suppress exceptions to avoid issues with `manage.py` commands such as + # `test`, `migrate`, `makemigrations`, or any custom user created commands + # where the database may not be ready. + try: + db_cleanup(immediate=True) + except DatabaseError: + _logger.debug( + "Could not access IDOM database at startup. Skipping cleanup..." + ) diff --git a/src/django_idom/components.py b/src/django_idom/components.py index 163eb47b..c57f87ae 100644 --- a/src/django_idom/components.py +++ b/src/django_idom/components.py @@ -5,15 +5,15 @@ from typing import Any, Callable, Protocol, Sequence, Union, cast, overload from django.contrib.staticfiles.finders import find +from django.core.cache import caches from django.http import HttpRequest from django.urls import reverse from django.views import View from idom import component, hooks, html, utils from idom.types import ComponentType, Key, VdomDict -from django_idom.config import IDOM_CACHE, IDOM_VIEW_COMPONENT_IFRAMES from django_idom.types import ViewComponentIframe -from django_idom.utils import _generate_obj_name, render_view +from django_idom.utils import generate_obj_name, render_view class _ViewComponentConstructor(Protocol): @@ -33,6 +33,8 @@ def _view_to_component( args: Sequence | None, kwargs: dict | None, ): + from django_idom.config import IDOM_VIEW_COMPONENT_IFRAMES + converted_view, set_converted_view = hooks.use_state( cast(Union[VdomDict, None], None) ) @@ -47,8 +49,8 @@ def _view_to_component( # Render the view render within a hook @hooks.use_effect( dependencies=[ - json.dumps(vars(_request), default=lambda x: _generate_obj_name(x)), - json.dumps([_args, _kwargs], default=lambda x: _generate_obj_name(x)), + json.dumps(vars(_request), default=lambda x: generate_obj_name(x)), + json.dumps([_args, _kwargs], default=lambda x: generate_obj_name(x)), ] ) async def async_render(): @@ -62,6 +64,7 @@ async def async_render(): set_converted_view( utils.html_to_vdom( response.content.decode("utf-8").strip(), + utils.del_html_head_body_transform, *transforms, strict=strict_parsing, ) @@ -130,7 +133,7 @@ def view_to_component( perfectly adhere to HTML5. Returns: - Callable: A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` + A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` and returns an IDOM component. """ @@ -197,6 +200,8 @@ def django_js(static_path: str, key: Key | None = None): def _cached_static_contents(static_path: str): + from django_idom.config import IDOM_CACHE + # Try to find the file within Django's static files abs_path = find(static_path) if not abs_path: @@ -208,12 +213,12 @@ def _cached_static_contents(static_path: str): # Cache is preferrable to `use_memo` due to multiprocessing capabilities last_modified_time = os.stat(abs_path).st_mtime cache_key = f"django_idom:static_contents:{static_path}" - file_contents = IDOM_CACHE.get(cache_key, version=int(last_modified_time)) + file_contents = caches[IDOM_CACHE].get(cache_key, version=int(last_modified_time)) if file_contents is None: with open(abs_path, encoding="utf-8") as static_file: file_contents = static_file.read() - IDOM_CACHE.delete(cache_key) - IDOM_CACHE.set( + caches[IDOM_CACHE].delete(cache_key) + caches[IDOM_CACHE].set( cache_key, file_contents, timeout=None, version=int(last_modified_time) ) diff --git a/src/django_idom/config.py b/src/django_idom/config.py index 0e1d4cd7..99d99c71 100644 --- a/src/django_idom/config.py +++ b/src/django_idom/config.py @@ -1,30 +1,46 @@ from __future__ import annotations -from typing import Dict - from django.conf import settings -from django.core.cache import DEFAULT_CACHE_ALIAS, BaseCache, caches +from django.core.cache import DEFAULT_CACHE_ALIAS +from django.db import DEFAULT_DB_ALIAS +from idom.config import IDOM_DEBUG_MODE from idom.core.types import ComponentConstructor -from django_idom.defaults import _DEFAULT_QUERY_POSTPROCESSOR from django_idom.types import Postprocessor, ViewComponentIframe +from django_idom.utils import import_dotted_path + +# Not user configurable settings +IDOM_DEBUG_MODE.set_current(getattr(settings, "DEBUG")) +IDOM_REGISTERED_COMPONENTS: dict[str, ComponentConstructor] = {} +IDOM_VIEW_COMPONENT_IFRAMES: dict[str, ViewComponentIframe] = {} -IDOM_REGISTERED_COMPONENTS: Dict[str, ComponentConstructor] = {} -IDOM_VIEW_COMPONENT_IFRAMES: Dict[str, ViewComponentIframe] = {} + +# Configurable through Django settings.py IDOM_WEBSOCKET_URL = getattr( settings, "IDOM_WEBSOCKET_URL", "idom/", ) -IDOM_WS_MAX_RECONNECT_TIMEOUT = getattr( +IDOM_RECONNECT_MAX = getattr( + settings, + "IDOM_RECONNECT_MAX", + 259200, # Default to 3 days +) +IDOM_CACHE: str = getattr( + settings, + "IDOM_CACHE", + DEFAULT_CACHE_ALIAS, +) +IDOM_DATABASE: str = getattr( settings, - "IDOM_WS_MAX_RECONNECT_TIMEOUT", - 604800, + "IDOM_DATABASE", + DEFAULT_DB_ALIAS, ) -IDOM_CACHE: BaseCache = ( - caches["idom"] - if "idom" in getattr(settings, "CACHES", {}) - else caches[DEFAULT_CACHE_ALIAS] +IDOM_DEFAULT_QUERY_POSTPROCESSOR: Postprocessor | None = import_dotted_path( + getattr( + settings, + "IDOM_DEFAULT_QUERY_POSTPROCESSOR", + "django_idom.utils.django_query_postprocessor", + ) ) -IDOM_DEFAULT_QUERY_POSTPROCESSOR: Postprocessor | None = _DEFAULT_QUERY_POSTPROCESSOR diff --git a/src/django_idom/decorators.py b/src/django_idom/decorators.py index 6d01c990..fb78f74e 100644 --- a/src/django_idom/decorators.py +++ b/src/django_idom/decorators.py @@ -5,7 +5,7 @@ from idom.core.types import ComponentType, VdomDict -from django_idom.hooks import use_websocket +from django_idom.hooks import use_scope def auth_required( @@ -27,9 +27,9 @@ def auth_required( def decorator(component): @wraps(component) def _wrapped_func(*args, **kwargs): - websocket = use_websocket() + scope = use_scope() - if getattr(websocket.scope["user"], auth_attribute): + if getattr(scope["user"], auth_attribute): return component(*args, **kwargs) return fallback(*args, **kwargs) if callable(fallback) else fallback diff --git a/src/django_idom/defaults.py b/src/django_idom/defaults.py deleted file mode 100644 index bc0530bb..00000000 --- a/src/django_idom/defaults.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from typing import Any, Callable - -from django.conf import settings - -from django_idom.utils import _import_dotted_path - - -_DEFAULT_QUERY_POSTPROCESSOR: Callable[..., Any] | None = _import_dotted_path( - getattr( - settings, - "IDOM_DEFAULT_QUERY_POSTPROCESSOR", - "django_idom.utils.django_query_postprocessor", - ) -) diff --git a/src/django_idom/hooks.py b/src/django_idom/hooks.py index 4455f779..ad40d301 100644 --- a/src/django_idom/hooks.py +++ b/src/django_idom/hooks.py @@ -15,18 +15,21 @@ from channels.db import database_sync_to_async as _database_sync_to_async from idom import use_callback, use_ref +from idom.backend.hooks import use_connection as _use_connection +from idom.backend.hooks import use_location as _use_location +from idom.backend.hooks import use_scope as _use_scope from idom.backend.types import Location -from idom.core.hooks import Context, create_context, use_context, use_effect, use_state +from idom.core.hooks import use_effect, use_state from django_idom.types import ( - IdomWebsocket, + Connection, Mutation, Query, QueryOptions, _Params, _Result, ) -from django_idom.utils import _generate_obj_name +from django_idom.utils import generate_obj_name _logger = logging.getLogger(__name__) @@ -34,7 +37,6 @@ Callable[..., Callable[..., Awaitable[Any]]], _database_sync_to_async, ) -WebsocketContext: Context[IdomWebsocket | None] = create_context(None) _REFETCH_CALLBACKS: DefaultDict[ Callable[..., Any], set[Callable[[], None]] ] = DefaultDict(set) @@ -42,16 +44,13 @@ def use_location() -> Location: """Get the current route as a `Location` object""" - # TODO: Use the browser's current page, rather than the WS route - scope = use_scope() - search = scope["query_string"].decode() - return Location(scope["path"], f"?{search}" if search else "") + return _use_location() def use_origin() -> str | None: """Get the current origin as a string. If the browser did not send an origin header, this will be None.""" - scope = use_scope() + scope = _use_scope() try: return next( ( @@ -67,15 +66,17 @@ def use_origin() -> str | None: def use_scope() -> dict[str, Any]: """Get the current ASGI scope dictionary""" - return use_websocket().scope + scope = _use_scope() + if isinstance(scope, dict): + return scope -def use_websocket() -> IdomWebsocket: - """Get the current IdomWebsocket object""" - websocket = use_context(WebsocketContext) - if websocket is None: - raise RuntimeError("No websocket. Are you running with a Django server?") - return websocket + raise TypeError(f"Expected scope to be a dict, got {type(scope)}") + + +def use_connection() -> Connection: + """Get the current `Connection` object""" + return _use_connection() @overload @@ -165,7 +166,7 @@ def execute_query() -> None: set_loading(False) set_error(e) _logger.exception( - f"Failed to execute query: {_generate_obj_name(query) or query}" + f"Failed to execute query: {generate_obj_name(query) or query}" ) return finally: @@ -208,7 +209,7 @@ def execute_mutation() -> None: set_loading(False) set_error(e) _logger.exception( - f"Failed to execute mutation: {_generate_obj_name(mutate) or mutate}" + f"Failed to execute mutation: {generate_obj_name(mutate) or mutate}" ) else: set_loading(False) diff --git a/src/django_idom/http/views.py b/src/django_idom/http/views.py index 57233bab..384c4740 100644 --- a/src/django_idom/http/views.py +++ b/src/django_idom/http/views.py @@ -1,35 +1,37 @@ import os from aiofile import async_open +from django.core.cache import caches from django.core.exceptions import SuspiciousOperation from django.http import HttpRequest, HttpResponse, HttpResponseNotFound -from idom.config import IDOM_WED_MODULES_DIR +from idom.config import IDOM_WEB_MODULES_DIR -from django_idom.config import IDOM_CACHE, IDOM_VIEW_COMPONENT_IFRAMES -from django_idom.utils import render_view +from django_idom.utils import create_cache_key, render_view async def web_modules_file(request: HttpRequest, file: str) -> HttpResponse: """Gets JavaScript required for IDOM modules at runtime. These modules are returned from cache if available.""" - web_modules_dir = IDOM_WED_MODULES_DIR.current + from django_idom.config import IDOM_CACHE + + web_modules_dir = IDOM_WEB_MODULES_DIR.current path = os.path.abspath(web_modules_dir.joinpath(*file.split("/"))) # Prevent attempts to walk outside of the web modules dir if str(web_modules_dir) != os.path.commonpath((path, web_modules_dir)): raise SuspiciousOperation( - "Attempt to access a directory outside of IDOM_WED_MODULES_DIR." + "Attempt to access a directory outside of IDOM_WEB_MODULES_DIR." ) # Fetch the file from cache, if available last_modified_time = os.stat(path).st_mtime - cache_key = f"django_idom:web_module:{str(path).lstrip(str(web_modules_dir))}" - response = await IDOM_CACHE.aget(cache_key, version=int(last_modified_time)) + cache_key = create_cache_key("web_module", str(path).lstrip(str(web_modules_dir))) + response = await caches[IDOM_CACHE].aget(cache_key, version=int(last_modified_time)) if response is None: async with async_open(path, "r") as fp: response = HttpResponse(await fp.read(), content_type="text/javascript") - await IDOM_CACHE.adelete(cache_key) - await IDOM_CACHE.aset( + await caches[IDOM_CACHE].adelete(cache_key) + await caches[IDOM_CACHE].aset( cache_key, response, timeout=None, version=int(last_modified_time) ) return response @@ -40,6 +42,8 @@ async def view_to_component_iframe( ) -> HttpResponse: """Returns a view that was registered by view_to_component. This view is intended to be used as iframe, for compatibility purposes.""" + from django_idom.config import IDOM_VIEW_COMPONENT_IFRAMES + # Get the view from IDOM_REGISTERED_IFRAMES iframe = IDOM_VIEW_COMPONENT_IFRAMES.get(view_path) if not iframe: diff --git a/src/django_idom/migrations/0001_initial.py b/src/django_idom/migrations/0001_initial.py new file mode 100644 index 00000000..3ad19d56 --- /dev/null +++ b/src/django_idom/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.1.3 on 2023-01-10 00:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="ComponentParams", + fields=[ + ( + "uuid", + models.UUIDField( + editable=False, primary_key=True, serialize=False, unique=True + ), + ), + ("data", models.BinaryField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/src/django_idom/migrations/0002_rename_created_at_componentparams_last_accessed.py b/src/django_idom/migrations/0002_rename_created_at_componentparams_last_accessed.py new file mode 100644 index 00000000..80207bce --- /dev/null +++ b/src/django_idom/migrations/0002_rename_created_at_componentparams_last_accessed.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.5 on 2023-01-11 01:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("django_idom", "0001_initial"), + ] + + operations = [ + migrations.RenameField( + model_name="componentparams", + old_name="created_at", + new_name="last_accessed", + ), + ] diff --git a/src/django_idom/migrations/0003_componentsession_delete_componentparams.py b/src/django_idom/migrations/0003_componentsession_delete_componentparams.py new file mode 100644 index 00000000..7a5c98e6 --- /dev/null +++ b/src/django_idom/migrations/0003_componentsession_delete_componentparams.py @@ -0,0 +1,28 @@ +# Generated by Django 4.1.6 on 2023-02-21 10:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("django_idom", "0002_rename_created_at_componentparams_last_accessed"), + ] + + operations = [ + migrations.CreateModel( + name="ComponentSession", + fields=[ + ( + "uuid", + models.UUIDField( + editable=False, primary_key=True, serialize=False, unique=True + ), + ), + ("params", models.BinaryField()), + ("last_accessed", models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.DeleteModel( + name="ComponentParams", + ), + ] diff --git a/src/django_idom/migrations/__init__.py b/src/django_idom/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/django_idom/models.py b/src/django_idom/models.py new file mode 100644 index 00000000..219866f2 --- /dev/null +++ b/src/django_idom/models.py @@ -0,0 +1,11 @@ +from django.db import models + + +class ComponentSession(models.Model): + """A model for storing component parameters. + All queries must be routed through `django_idom.config.IDOM_DATABASE`. + """ + + uuid = models.UUIDField(primary_key=True, editable=False, unique=True) # type: ignore + params = models.BinaryField(editable=False) # type: ignore + last_accessed = models.DateTimeField(auto_now_add=True) # type: ignore diff --git a/src/django_idom/templates/idom/component.html b/src/django_idom/templates/idom/component.html index fc8ba6f8..7fd82c22 100644 --- a/src/django_idom/templates/idom/component.html +++ b/src/django_idom/templates/idom/component.html @@ -2,13 +2,12 @@
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py index b6bb6868..da13b352 100644 --- a/src/django_idom/templatetags/idom.py +++ b/src/django_idom/templatetags/idom.py @@ -1,13 +1,13 @@ -import json -from typing import Any -from urllib.parse import urlencode from uuid import uuid4 +import dill as pickle from django import template from django.urls import reverse -from django_idom.config import IDOM_WEBSOCKET_URL, IDOM_WS_MAX_RECONNECT_TIMEOUT -from django_idom.utils import _register_component +from django_idom import models +from django_idom.config import IDOM_RECONNECT_MAX, IDOM_WEBSOCKET_URL +from django_idom.types import ComponentParamData +from django_idom.utils import _register_component, func_has_params IDOM_WEB_MODULES_URL = reverse("idom:web_modules", args=["x"])[:-1][1:] @@ -15,14 +15,15 @@ @register.inclusion_tag("idom/component.html") -def component(dotted_path: str, **kwargs: Any): +def component(dotted_path: str, *args, **kwargs): """This tag is used to embed an existing IDOM component into your HTML template. Args: dotted_path: The dotted path to the component to render. + *args: The positional arguments to provide to the component. Keyword Args: - **kwargs: The keyword arguments to pass to the component. + **kwargs: The keyword arguments to provide to the component. Example :: @@ -34,17 +35,29 @@ def component(dotted_path: str, **kwargs: Any): """ - _register_component(dotted_path) - + component = _register_component(dotted_path) + uuid = uuid4().hex class_ = kwargs.pop("class", "") - json_kwargs = json.dumps(kwargs, separators=(",", ":")) + kwargs.pop("key", "") # `key` is effectively useless for the root node + + # Store the component's args/kwargs in the database if needed + # This will be fetched by the websocket consumer later + try: + if func_has_params(component, *args, **kwargs): + params = ComponentParamData(args, kwargs) + model = models.ComponentSession(uuid=uuid, params=pickle.dumps(params)) + model.full_clean() + model.save() + except TypeError as e: + raise TypeError( + f"The provided parameters are incompatible with component '{dotted_path}'." + ) from e return { "class": class_, "idom_websocket_url": IDOM_WEBSOCKET_URL, "idom_web_modules_url": IDOM_WEB_MODULES_URL, - "idom_ws_max_reconnect_timeout": IDOM_WS_MAX_RECONNECT_TIMEOUT, - "idom_mount_uuid": uuid4().hex, - "idom_component_id": dotted_path, - "idom_component_params": urlencode({"kwargs": json_kwargs}), + "idom_reconnect_max": IDOM_RECONNECT_MAX, + "idom_mount_uuid": uuid, + "idom_component_path": f"{dotted_path}/{uuid}/", } diff --git a/src/django_idom/types.py b/src/django_idom/types.py index 7705c98b..1427d62a 100644 --- a/src/django_idom/types.py +++ b/src/django_idom/types.py @@ -6,6 +6,7 @@ Awaitable, Callable, Generic, + MutableMapping, Optional, Protocol, Sequence, @@ -16,12 +17,23 @@ from django.db.models.base import Model from django.db.models.query import QuerySet from django.views.generic import View +from idom.types import Connection as _Connection from typing_extensions import ParamSpec -from django_idom.defaults import _DEFAULT_QUERY_POSTPROCESSOR - -__all__ = ["_Result", "_Params", "_Data", "IdomWebsocket", "Query", "Mutation"] +__all__ = [ + "_Result", + "_Params", + "_Data", + "ComponentWebsocket", + "Query", + "Mutation", + "Connection", + "ViewComponentIframe", + "Postprocessor", + "QueryOptions", + "ComponentParamData", +] _Result = TypeVar("_Result", bound=Union[Model, QuerySet[Any]]) _Params = ParamSpec("_Params") @@ -29,13 +41,15 @@ @dataclass -class IdomWebsocket: - """Websocket returned by the `use_websocket` hook.""" +class ComponentWebsocket: + """Carrier type for the `use_connection` hook.""" - scope: dict close: Callable[[Optional[int]], Awaitable[None]] disconnect: Callable[[int], Awaitable[None]] - view_id: str + dotted_path: str + + +Connection = _Connection[ComponentWebsocket] @dataclass @@ -74,7 +88,9 @@ def __call__(self, data: Any) -> Any: class QueryOptions: """Configuration options that can be provided to `use_query`.""" - postprocessor: Postprocessor | None = _DEFAULT_QUERY_POSTPROCESSOR + from django_idom.config import IDOM_DEFAULT_QUERY_POSTPROCESSOR + + postprocessor: Postprocessor | None = IDOM_DEFAULT_QUERY_POSTPROCESSOR """A callable that can modify the query `data` after the query has been executed. The first argument of postprocessor must be the query `data`. All proceeding arguments @@ -87,5 +103,14 @@ class QueryOptions: additionally can be configured via `postprocessor_kwargs` to recursively fetch `many_to_many` and `many_to_one` fields.""" - postprocessor_kwargs: dict[str, Any] = field(default_factory=lambda: {}) + postprocessor_kwargs: MutableMapping[str, Any] = field(default_factory=lambda: {}) """Keyworded arguments directly passed into the `postprocessor` for configuration.""" + + +@dataclass +class ComponentParamData: + """Container used for serializing component parameters. + This dataclass is pickled & stored in the database, then unpickled when needed.""" + + args: Sequence + kwargs: MutableMapping[str, Any] diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 9c76a9f9..c360bddd 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -1,21 +1,25 @@ from __future__ import annotations import contextlib +import inspect import logging import os import re +from datetime import datetime, timedelta from fnmatch import fnmatch from importlib import import_module from inspect import iscoroutinefunction from typing import Any, Callable, Sequence from channels.db import database_sync_to_async +from django.core.cache import caches from django.db.models import ManyToManyField, prefetch_related_objects from django.db.models.base import Model from django.db.models.fields.reverse_related import ManyToOneRel from django.db.models.query import QuerySet from django.http import HttpRequest, HttpResponse from django.template import engines +from django.utils import timezone from django.utils.encoding import smart_str from django.views import View @@ -34,6 +38,7 @@ + _component_kwargs + r"\s*%}" ) +DATE_FORMAT = "%Y-%m-%d %H:%M:%S" async def render_view( @@ -74,17 +79,21 @@ async def render_view( return response -def _register_component(dotted_path: str) -> None: +def _register_component(dotted_path: str) -> Callable: + """Adds a component to the mapping of registered components. + This should only be called on startup to maintain synchronization during mulitprocessing. + """ from django_idom.config import IDOM_REGISTERED_COMPONENTS if dotted_path in IDOM_REGISTERED_COMPONENTS: - return + return IDOM_REGISTERED_COMPONENTS[dotted_path] - IDOM_REGISTERED_COMPONENTS[dotted_path] = _import_dotted_path(dotted_path) + IDOM_REGISTERED_COMPONENTS[dotted_path] = import_dotted_path(dotted_path) _logger.debug("IDOM has registered component %s", dotted_path) + return IDOM_REGISTERED_COMPONENTS[dotted_path] -def _import_dotted_path(dotted_path: str) -> Callable: +def import_dotted_path(dotted_path: str) -> Callable: """Imports a dotted path and returns the callable.""" module_name, component_name = dotted_path.rsplit(".", 1) @@ -99,24 +108,28 @@ def _import_dotted_path(dotted_path: str) -> Callable: class ComponentPreloader: - def register_all(self): + """Preloads all IDOM components found within Django templates. + This should only be `run` once on startup to maintain synchronization during mulitprocessing. + """ + + def run(self): """Registers all IDOM components found within Django templates.""" # Get all template folder paths - paths = self._get_paths() + paths = self.get_paths() # Get all HTML template files - templates = self._get_templates(paths) + templates = self.get_templates(paths) # Get all components - components = self._get_components(templates) + components = self.get_components(templates) # Register all components - self._register_components(components) + self.register_components(components) - def _get_loaders(self): + def get_loaders(self): """Obtains currently configured template loaders.""" template_source_loaders = [] for e in engines.all(): if hasattr(e, "engine"): template_source_loaders.extend( - e.engine.get_template_loaders(e.engine.loaders) # type: ignore + e.engine.get_template_loaders(e.engine.loaders) ) loaders = [] for loader in template_source_loaders: @@ -126,10 +139,10 @@ def _get_loaders(self): loaders.append(loader) return loaders - def _get_paths(self) -> set[str]: + def get_paths(self) -> set[str]: """Obtains a set of all template directories.""" paths: set[str] = set() - for loader in self._get_loaders(): + for loader in self.get_loaders(): with contextlib.suppress(ImportError, AttributeError, TypeError): module = import_module(loader.__module__) get_template_sources = getattr(module, "get_template_sources", None) @@ -138,7 +151,7 @@ def _get_paths(self) -> set[str]: paths.update(smart_str(origin) for origin in get_template_sources("")) return paths - def _get_templates(self, paths: set[str]) -> set[str]: + def get_templates(self, paths: set[str]) -> set[str]: """Obtains a set of all HTML template paths.""" extensions = [".html"] templates: set[str] = set() @@ -153,7 +166,7 @@ def _get_templates(self, paths: set[str]) -> set[str]: return templates - def _get_components(self, templates: set[str]) -> set[str]: + def get_components(self, templates: set[str]) -> set[str]: """Obtains a set of all IDOM components by parsing HTML templates.""" components: set[str] = set() for template in templates: @@ -177,7 +190,7 @@ def _get_components(self, templates: set[str]) -> set[str]: ) return components - def _register_components(self, components: set[str]) -> None: + def register_components(self, components: set[str]) -> None: """Registers all IDOM components in an iterable.""" for component in components: try: @@ -194,7 +207,7 @@ def _register_components(self, components: set[str]) -> None: ) -def _generate_obj_name(object: Any) -> str | None: +def generate_obj_name(object: Any) -> str | None: """Makes a best effort to create a name for an object. Useful for JSON serialization of Python objects.""" if hasattr(object, "__module__"): @@ -210,7 +223,18 @@ def django_query_postprocessor( ) -> QuerySet | Model: """Recursively fetch all fields within a `Model` or `QuerySet` to ensure they are not performed lazily. - Some behaviors can be modified through `query_options` attributes.""" + Behaviors can be modified through `QueryOptions` within your `use_query` hook. + + Args: + data: The `Model` or `QuerySet` to recursively fetch fields from. + + Keyword Args: + many_to_many: Whether or not to recursively fetch `ManyToManyField` relationships. + many_to_one: Whether or not to recursively fetch `ForeignKey` relationships. + + Returns: + The `Model` or `QuerySet` with all fields fetched. + """ # `QuerySet`, which is an iterable of `Model`/`QuerySet` instances # https://github.com/typeddjango/django-stubs/issues/704 @@ -232,7 +256,7 @@ def django_query_postprocessor( getattr(data, field.name) if many_to_one and type(field) == ManyToOneRel: - prefetch_fields.append(f"{field.name}_set") + prefetch_fields.append(field.related_name or f"{field.name}_set") elif many_to_many and isinstance(field, ManyToManyField): prefetch_fields.append(field.name) @@ -257,3 +281,61 @@ def django_query_postprocessor( ) return data + + +def func_has_params(func: Callable, *args, **kwargs) -> bool: + """Checks if a function has any args or kwarg parameters. + + Can optionally validate whether a set of args/kwargs would work on the given function. + """ + signature = inspect.signature(func) + + # Check if the function has any args/kwargs + if not args and not kwargs: + return str(signature) != "()" + + # Check if the function has the given args/kwargs + signature.bind(*args, **kwargs) + return True + + +def create_cache_key(*args): + """Creates a cache key string that starts with `django_idom` contains + all *args separated by `:`.""" + + if not args: + raise ValueError("At least one argument is required to create a cache key.") + + return f"django_idom:{':'.join(str(arg) for arg in args)}" + + +def db_cleanup(immediate: bool = False): + """Deletes expired component parameters from the database. + This function may be expanded in the future to include additional cleanup tasks.""" + from .config import IDOM_CACHE, IDOM_DATABASE, IDOM_RECONNECT_MAX + from .models import ComponentSession + + cache_key: str = create_cache_key("last_cleaned") + now_str: str = datetime.strftime(timezone.now(), DATE_FORMAT) + cleaned_at_str: str = caches[IDOM_CACHE].get(cache_key) + cleaned_at: datetime = timezone.make_aware( + datetime.strptime(cleaned_at_str or now_str, DATE_FORMAT) + ) + clean_needed_by = cleaned_at + timedelta(seconds=IDOM_RECONNECT_MAX) + expires_by: datetime = timezone.now() - timedelta(seconds=IDOM_RECONNECT_MAX) + + # Component params exist in the DB, but we don't know when they were last cleaned + if not cleaned_at_str and ComponentSession.objects.using(IDOM_DATABASE).all(): + _logger.warning( + "IDOM has detected component sessions in the database, " + "but no timestamp was found in cache. This may indicate that " + "the cache has been cleared." + ) + + # Delete expired component parameters + # Use timestamps in cache (`cleaned_at_str`) as a no-dependency rate limiter + if immediate or not cleaned_at_str or timezone.now() >= clean_needed_by: + ComponentSession.objects.using(IDOM_DATABASE).filter( + last_accessed__lte=expires_by + ).delete() + caches[IDOM_CACHE].set(cache_key, now_str) diff --git a/src/django_idom/websocket/consumer.py b/src/django_idom/websocket/consumer.py index c4c78971..e0cf56c4 100644 --- a/src/django_idom/websocket/consumer.py +++ b/src/django_idom/websocket/consumer.py @@ -1,19 +1,23 @@ """Anything used to construct a websocket endpoint""" +from __future__ import annotations + import asyncio -import json import logging -from typing import Any -from urllib.parse import parse_qsl +from datetime import timedelta +from typing import Any, MutableMapping, Sequence +import dill as pickle from channels.auth import login from channels.db import database_sync_to_async as convert_to_async from channels.generic.websocket import AsyncJsonWebsocketConsumer -from idom.core.layout import Layout, LayoutEvent -from idom.core.serve import serve_json_patch +from django.utils import timezone +from idom.backend.hooks import ConnectionContext +from idom.backend.types import Connection, Location +from idom.core.layout import Layout +from idom.core.serve import serve_layout -from django_idom.config import IDOM_REGISTERED_COMPONENTS -from django_idom.hooks import WebsocketContext -from django_idom.types import IdomWebsocket +from django_idom.types import ComponentParamData, ComponentWebsocket +from django_idom.utils import db_cleanup, func_has_params _logger = logging.getLogger(__name__) @@ -23,9 +27,11 @@ class IdomAsyncWebsocketConsumer(AsyncJsonWebsocketConsumer): """Communicates with the browser to perform actions on-demand.""" async def connect(self) -> None: + from django.contrib.auth.models import AbstractBaseUser + await super().connect() - user = self.scope.get("user") + user: AbstractBaseUser = self.scope.get("user") if user and user.is_authenticated: try: await login(self.scope, user) @@ -44,26 +50,74 @@ async def disconnect(self, code: int) -> None: self._idom_dispatcher_future.cancel() await super().disconnect(code) - async def receive_json(self, content: Any, **kwargs: Any) -> None: - await self._idom_recv_queue.put(LayoutEvent(**content)) + async def receive_json(self, content: Any, **_) -> None: + await self._idom_recv_queue.put(content) async def _run_dispatch_loop(self): - view_id = self.scope["url_route"]["kwargs"]["view_id"] - + from django_idom import models + from django_idom.config import ( + IDOM_DATABASE, + IDOM_RECONNECT_MAX, + IDOM_REGISTERED_COMPONENTS, + ) + + scope = self.scope + dotted_path = scope["url_route"]["kwargs"]["dotted_path"] + uuid = scope["url_route"]["kwargs"]["uuid"] + search = scope["query_string"].decode() + self._idom_recv_queue: asyncio.Queue = asyncio.Queue() + connection = Connection( # For `use_connection` + scope=scope, + location=Location( + pathname=scope["path"], + search=f"?{search}" if (search and (search != "undefined")) else "", + ), + carrier=ComponentWebsocket(self.close, self.disconnect, dotted_path), + ) + now = timezone.now() + component_args: Sequence[Any] = tuple() + component_kwargs: MutableMapping[str, Any] = {} + + # Verify the component has already been registered try: - component_constructor = IDOM_REGISTERED_COMPONENTS[view_id] + component_constructor = IDOM_REGISTERED_COMPONENTS[dotted_path] except KeyError: - _logger.warning(f"Unknown IDOM view ID {view_id!r}") + _logger.warning( + f"Attempt to access invalid IDOM component: {dotted_path!r}" + ) return - query_dict = dict(parse_qsl(self.scope["query_string"].decode())) - component_kwargs = json.loads(query_dict.get("kwargs", "{}")) - - # Provide developer access to parts of this websocket - socket = IdomWebsocket(self.scope, self.close, self.disconnect, view_id) - + # Fetch the component's args/kwargs from the database, if needed try: - component_instance = component_constructor(**component_kwargs) + if func_has_params(component_constructor): + try: + # Always clean up expired entries first + await convert_to_async(db_cleanup)() + + # Get the queries from a DB + params_query = await models.ComponentSession.objects.using( + IDOM_DATABASE + ).aget( + uuid=uuid, + last_accessed__gt=now - timedelta(seconds=IDOM_RECONNECT_MAX), + ) + params_query.last_accessed = timezone.now() + await convert_to_async(params_query.save)() + except models.ComponentSession.DoesNotExist: + _logger.warning( + f"Browser has attempted to access '{dotted_path}', " + f"but the component has already expired beyond IDOM_RECONNECT_MAX. " + "If this was expected, this warning can be ignored." + ) + return + component_params: ComponentParamData = pickle.loads(params_query.params) + component_args = component_params.args + component_kwargs = component_params.kwargs + + # Generate the initial component instance + component_instance = component_constructor( + *component_args, **component_kwargs + ) except Exception: _logger.exception( f"Failed to construct component {component_constructor} " @@ -71,12 +125,12 @@ async def _run_dispatch_loop(self): ) return - self._idom_recv_queue = recv_queue = asyncio.Queue() # type: ignore + # Begin serving the IDOM component try: - await serve_json_patch( - Layout(WebsocketContext(component_instance, value=socket)), + await serve_layout( + Layout(ConnectionContext(component_instance, value=connection)), self.send_json, - recv_queue.get, + self._idom_recv_queue.get, ) except Exception: await self.close() diff --git a/src/django_idom/websocket/paths.py b/src/django_idom/websocket/paths.py index f337c83e..fab68aee 100644 --- a/src/django_idom/websocket/paths.py +++ b/src/django_idom/websocket/paths.py @@ -6,7 +6,7 @@ IDOM_WEBSOCKET_PATH = path( - f"{IDOM_WEBSOCKET_URL}/", IdomAsyncWebsocketConsumer.as_asgi() + f"{IDOM_WEBSOCKET_URL}//", IdomAsyncWebsocketConsumer.as_asgi() ) """A URL path for :class:`IdomAsyncWebsocketConsumer`. diff --git a/src/django_reactpy/static/django_idom/client.js b/src/django_reactpy/static/django_idom/client.js new file mode 100644 index 00000000..b385c0a5 --- /dev/null +++ b/src/django_reactpy/static/django_idom/client.js @@ -0,0 +1,10211 @@ +function getDefaultExportFromCjs(x) { + return x && + x.__esModule && + Object.prototype.hasOwnProperty.call(x, "default") + ? x["default"] + : x; +} + +var reactExports = {}; +var react = { + get exports() { + return reactExports; + }, + set exports(v) { + reactExports = v; + }, +}; + +var react_production_min = {}; + +/* +object-assign +(c) Sindre Sorhus +@license MIT +*/ + +var objectAssign; +var hasRequiredObjectAssign; + +function requireObjectAssign() { + if (hasRequiredObjectAssign) return objectAssign; + hasRequiredObjectAssign = 1; + /* eslint-disable no-unused-vars */ + var getOwnPropertySymbols = Object.getOwnPropertySymbols; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var propIsEnumerable = Object.prototype.propertyIsEnumerable; + + function toObject(val) { + if (val === null || val === undefined) { + throw new TypeError( + "Object.assign cannot be called with null or undefined" + ); + } + + return Object(val); + } + + function shouldUseNative() { + try { + if (!Object.assign) { + return false; + } + + // Detect buggy property enumeration order in older V8 versions. + + // https://bugs.chromium.org/p/v8/issues/detail?id=4118 + var test1 = new String("abc"); // eslint-disable-line no-new-wrappers + test1[5] = "de"; + if (Object.getOwnPropertyNames(test1)[0] === "5") { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test2 = {}; + for (var i = 0; i < 10; i++) { + test2["_" + String.fromCharCode(i)] = i; + } + var order2 = Object.getOwnPropertyNames(test2).map(function (n) { + return test2[n]; + }); + if (order2.join("") !== "0123456789") { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test3 = {}; + "abcdefghijklmnopqrst".split("").forEach(function (letter) { + test3[letter] = letter; + }); + if ( + Object.keys(Object.assign({}, test3)).join("") !== + "abcdefghijklmnopqrst" + ) { + return false; + } + + return true; + } catch (err) { + // We don't expect any of the above to throw, but better to be safe. + return false; + } + } + + objectAssign = shouldUseNative() + ? Object.assign + : function (target, source) { + var from; + var to = toObject(target); + var symbols; + + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); + + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + + if (getOwnPropertySymbols) { + symbols = getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } + + return to; + }; + return objectAssign; +} + +/** @license React v17.0.2 + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var hasRequiredReact_production_min; + +function requireReact_production_min() { + if (hasRequiredReact_production_min) return react_production_min; + hasRequiredReact_production_min = 1; + var l = requireObjectAssign(), + n = 60103, + p = 60106; + react_production_min.Fragment = 60107; + react_production_min.StrictMode = 60108; + react_production_min.Profiler = 60114; + var q = 60109, + r = 60110, + t = 60112; + react_production_min.Suspense = 60113; + var u = 60115, + v = 60116; + if ("function" === typeof Symbol && Symbol.for) { + var w = Symbol.for; + n = w("react.element"); + p = w("react.portal"); + react_production_min.Fragment = w("react.fragment"); + react_production_min.StrictMode = w("react.strict_mode"); + react_production_min.Profiler = w("react.profiler"); + q = w("react.provider"); + r = w("react.context"); + t = w("react.forward_ref"); + react_production_min.Suspense = w("react.suspense"); + u = w("react.memo"); + v = w("react.lazy"); + } + var x = "function" === typeof Symbol && Symbol.iterator; + function y(a) { + if (null === a || "object" !== typeof a) return null; + a = (x && a[x]) || a["@@iterator"]; + return "function" === typeof a ? a : null; + } + function z(a) { + for ( + var b = + "https://reactjs.org/docs/error-decoder.html?invariant=" + + a, + c = 1; + c < arguments.length; + c++ + ) + b += "&args[]=" + encodeURIComponent(arguments[c]); + return ( + "Minified React error #" + + a + + "; visit " + + b + + " for the full message or use the non-minified dev environment for full errors and additional helpful warnings." + ); + } + var A = { + isMounted: function () { + return !1; + }, + enqueueForceUpdate: function () {}, + enqueueReplaceState: function () {}, + enqueueSetState: function () {}, + }, + B = {}; + function C(a, b, c) { + this.props = a; + this.context = b; + this.refs = B; + this.updater = c || A; + } + C.prototype.isReactComponent = {}; + C.prototype.setState = function (a, b) { + if ("object" !== typeof a && "function" !== typeof a && null != a) + throw Error(z(85)); + this.updater.enqueueSetState(this, a, b, "setState"); + }; + C.prototype.forceUpdate = function (a) { + this.updater.enqueueForceUpdate(this, a, "forceUpdate"); + }; + function D() {} + D.prototype = C.prototype; + function E(a, b, c) { + this.props = a; + this.context = b; + this.refs = B; + this.updater = c || A; + } + var F = (E.prototype = new D()); + F.constructor = E; + l(F, C.prototype); + F.isPureReactComponent = !0; + var G = { current: null }, + H = Object.prototype.hasOwnProperty, + I = { key: !0, ref: !0, __self: !0, __source: !0 }; + function J(a, b, c) { + var e, + d = {}, + k = null, + h = null; + if (null != b) + for (e in (void 0 !== b.ref && (h = b.ref), + void 0 !== b.key && (k = "" + b.key), + b)) + H.call(b, e) && !I.hasOwnProperty(e) && (d[e] = b[e]); + var g = arguments.length - 2; + if (1 === g) d.children = c; + else if (1 < g) { + for (var f = Array(g), m = 0; m < g; m++) f[m] = arguments[m + 2]; + d.children = f; + } + if (a && a.defaultProps) + for (e in ((g = a.defaultProps), g)) + void 0 === d[e] && (d[e] = g[e]); + return { + $$typeof: n, + type: a, + key: k, + ref: h, + props: d, + _owner: G.current, + }; + } + function K(a, b) { + return { + $$typeof: n, + type: a.type, + key: b, + ref: a.ref, + props: a.props, + _owner: a._owner, + }; + } + function L(a) { + return "object" === typeof a && null !== a && a.$$typeof === n; + } + function escape(a) { + var b = { "=": "=0", ":": "=2" }; + return ( + "$" + + a.replace(/[=:]/g, function (a) { + return b[a]; + }) + ); + } + var M = /\/+/g; + function N(a, b) { + return "object" === typeof a && null !== a && null != a.key + ? escape("" + a.key) + : b.toString(36); + } + function O(a, b, c, e, d) { + var k = typeof a; + if ("undefined" === k || "boolean" === k) a = null; + var h = !1; + if (null === a) h = !0; + else + switch (k) { + case "string": + case "number": + h = !0; + break; + case "object": + switch (a.$$typeof) { + case n: + case p: + h = !0; + } + } + if (h) + return ( + (h = a), + (d = d(h)), + (a = "" === e ? "." + N(h, 0) : e), + Array.isArray(d) + ? ((c = ""), + null != a && (c = a.replace(M, "$&/") + "/"), + O(d, b, c, "", function (a) { + return a; + })) + : null != d && + (L(d) && + (d = K( + d, + c + + (!d.key || (h && h.key === d.key) + ? "" + : ("" + d.key).replace(M, "$&/") + + "/") + + a + )), + b.push(d)), + 1 + ); + h = 0; + e = "" === e ? "." : e + ":"; + if (Array.isArray(a)) + for (var g = 0; g < a.length; g++) { + k = a[g]; + var f = e + N(k, g); + h += O(k, b, c, f, d); + } + else if (((f = y(a)), "function" === typeof f)) + for (a = f.call(a), g = 0; !(k = a.next()).done; ) + (k = k.value), (f = e + N(k, g++)), (h += O(k, b, c, f, d)); + else if ("object" === k) + throw ( + ((b = "" + a), + Error( + z( + 31, + "[object Object]" === b + ? "object with keys {" + + Object.keys(a).join(", ") + + "}" + : b + ) + )) + ); + return h; + } + function P(a, b, c) { + if (null == a) return a; + var e = [], + d = 0; + O(a, e, "", "", function (a) { + return b.call(c, a, d++); + }); + return e; + } + function Q(a) { + if (-1 === a._status) { + var b = a._result; + b = b(); + a._status = 0; + a._result = b; + b.then( + function (b) { + 0 === a._status && + ((b = b.default), (a._status = 1), (a._result = b)); + }, + function (b) { + 0 === a._status && ((a._status = 2), (a._result = b)); + } + ); + } + if (1 === a._status) return a._result; + throw a._result; + } + var R = { current: null }; + function S() { + var a = R.current; + if (null === a) throw Error(z(321)); + return a; + } + var T = { + ReactCurrentDispatcher: R, + ReactCurrentBatchConfig: { transition: 0 }, + ReactCurrentOwner: G, + IsSomeRendererActing: { current: !1 }, + assign: l, + }; + react_production_min.Children = { + map: P, + forEach: function (a, b, c) { + P( + a, + function () { + b.apply(this, arguments); + }, + c + ); + }, + count: function (a) { + var b = 0; + P(a, function () { + b++; + }); + return b; + }, + toArray: function (a) { + return ( + P(a, function (a) { + return a; + }) || [] + ); + }, + only: function (a) { + if (!L(a)) throw Error(z(143)); + return a; + }, + }; + react_production_min.Component = C; + react_production_min.PureComponent = E; + react_production_min.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = T; + react_production_min.cloneElement = function (a, b, c) { + if (null === a || void 0 === a) throw Error(z(267, a)); + var e = l({}, a.props), + d = a.key, + k = a.ref, + h = a._owner; + if (null != b) { + void 0 !== b.ref && ((k = b.ref), (h = G.current)); + void 0 !== b.key && (d = "" + b.key); + if (a.type && a.type.defaultProps) var g = a.type.defaultProps; + for (f in b) + H.call(b, f) && + !I.hasOwnProperty(f) && + (e[f] = void 0 === b[f] && void 0 !== g ? g[f] : b[f]); + } + var f = arguments.length - 2; + if (1 === f) e.children = c; + else if (1 < f) { + g = Array(f); + for (var m = 0; m < f; m++) g[m] = arguments[m + 2]; + e.children = g; + } + return { + $$typeof: n, + type: a.type, + key: d, + ref: k, + props: e, + _owner: h, + }; + }; + react_production_min.createContext = function (a, b) { + void 0 === b && (b = null); + a = { + $$typeof: r, + _calculateChangedBits: b, + _currentValue: a, + _currentValue2: a, + _threadCount: 0, + Provider: null, + Consumer: null, + }; + a.Provider = { $$typeof: q, _context: a }; + return (a.Consumer = a); + }; + react_production_min.createElement = J; + react_production_min.createFactory = function (a) { + var b = J.bind(null, a); + b.type = a; + return b; + }; + react_production_min.createRef = function () { + return { current: null }; + }; + react_production_min.forwardRef = function (a) { + return { $$typeof: t, render: a }; + }; + react_production_min.isValidElement = L; + react_production_min.lazy = function (a) { + return { $$typeof: v, _payload: { _status: -1, _result: a }, _init: Q }; + }; + react_production_min.memo = function (a, b) { + return { $$typeof: u, type: a, compare: void 0 === b ? null : b }; + }; + react_production_min.useCallback = function (a, b) { + return S().useCallback(a, b); + }; + react_production_min.useContext = function (a, b) { + return S().useContext(a, b); + }; + react_production_min.useDebugValue = function () {}; + react_production_min.useEffect = function (a, b) { + return S().useEffect(a, b); + }; + react_production_min.useImperativeHandle = function (a, b, c) { + return S().useImperativeHandle(a, b, c); + }; + react_production_min.useLayoutEffect = function (a, b) { + return S().useLayoutEffect(a, b); + }; + react_production_min.useMemo = function (a, b) { + return S().useMemo(a, b); + }; + react_production_min.useReducer = function (a, b, c) { + return S().useReducer(a, b, c); + }; + react_production_min.useRef = function (a) { + return S().useRef(a); + }; + react_production_min.useState = function (a) { + return S().useState(a); + }; + react_production_min.version = "17.0.2"; + return react_production_min; +} + +(function (module) { + { + module.exports = requireReact_production_min(); + } +})(react); + +var React = /*@__PURE__*/ getDefaultExportFromCjs(reactExports); + +var reactDomExports = {}; +var reactDom = { + get exports() { + return reactDomExports; + }, + set exports(v) { + reactDomExports = v; + }, +}; + +var reactDom_production_min = {}; + +var schedulerExports = {}; +var scheduler = { + get exports() { + return schedulerExports; + }, + set exports(v) { + schedulerExports = v; + }, +}; + +var scheduler_production_min = {}; + +/** @license React v0.20.2 + * scheduler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var hasRequiredScheduler_production_min; + +function requireScheduler_production_min() { + if (hasRequiredScheduler_production_min) return scheduler_production_min; + hasRequiredScheduler_production_min = 1; + (function (exports) { + var f, g, h, k; + if ( + "object" === typeof performance && + "function" === typeof performance.now + ) { + var l = performance; + exports.unstable_now = function () { + return l.now(); + }; + } else { + var p = Date, + q = p.now(); + exports.unstable_now = function () { + return p.now() - q; + }; + } + if ( + "undefined" === typeof window || + "function" !== typeof MessageChannel + ) { + var t = null, + u = null, + w = function () { + if (null !== t) + try { + var a = exports.unstable_now(); + t(!0, a); + t = null; + } catch (b) { + throw (setTimeout(w, 0), b); + } + }; + f = function (a) { + null !== t ? setTimeout(f, 0, a) : ((t = a), setTimeout(w, 0)); + }; + g = function (a, b) { + u = setTimeout(a, b); + }; + h = function () { + clearTimeout(u); + }; + exports.unstable_shouldYield = function () { + return !1; + }; + k = exports.unstable_forceFrameRate = function () {}; + } else { + var x = window.setTimeout, + y = window.clearTimeout; + if ("undefined" !== typeof console) { + var z = window.cancelAnimationFrame; + "function" !== typeof window.requestAnimationFrame && + console.error( + "This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills" + ); + "function" !== typeof z && + console.error( + "This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://reactjs.org/link/react-polyfills" + ); + } + var A = !1, + B = null, + C = -1, + D = 5, + E = 0; + exports.unstable_shouldYield = function () { + return exports.unstable_now() >= E; + }; + k = function () {}; + exports.unstable_forceFrameRate = function (a) { + 0 > a || 125 < a + ? console.error( + "forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported" + ) + : (D = 0 < a ? Math.floor(1e3 / a) : 5); + }; + var F = new MessageChannel(), + G = F.port2; + F.port1.onmessage = function () { + if (null !== B) { + var a = exports.unstable_now(); + E = a + D; + try { + B(!0, a) ? G.postMessage(null) : ((A = !1), (B = null)); + } catch (b) { + throw (G.postMessage(null), b); + } + } else A = !1; + }; + f = function (a) { + B = a; + A || ((A = !0), G.postMessage(null)); + }; + g = function (a, b) { + C = x(function () { + a(exports.unstable_now()); + }, b); + }; + h = function () { + y(C); + C = -1; + }; + } + function H(a, b) { + var c = a.length; + a.push(b); + a: for (;;) { + var d = (c - 1) >>> 1, + e = a[d]; + if (void 0 !== e && 0 < I(e, b)) + (a[d] = b), (a[c] = e), (c = d); + else break a; + } + } + function J(a) { + a = a[0]; + return void 0 === a ? null : a; + } + function K(a) { + var b = a[0]; + if (void 0 !== b) { + var c = a.pop(); + if (c !== b) { + a[0] = c; + a: for (var d = 0, e = a.length; d < e; ) { + var m = 2 * (d + 1) - 1, + n = a[m], + v = m + 1, + r = a[v]; + if (void 0 !== n && 0 > I(n, c)) + void 0 !== r && 0 > I(r, n) + ? ((a[d] = r), (a[v] = c), (d = v)) + : ((a[d] = n), (a[m] = c), (d = m)); + else if (void 0 !== r && 0 > I(r, c)) + (a[d] = r), (a[v] = c), (d = v); + else break a; + } + } + return b; + } + return null; + } + function I(a, b) { + var c = a.sortIndex - b.sortIndex; + return 0 !== c ? c : a.id - b.id; + } + var L = [], + M = [], + N = 1, + O = null, + P = 3, + Q = !1, + R = !1, + S = !1; + function T(a) { + for (var b = J(M); null !== b; ) { + if (null === b.callback) K(M); + else if (b.startTime <= a) + K(M), (b.sortIndex = b.expirationTime), H(L, b); + else break; + b = J(M); + } + } + function U(a) { + S = !1; + T(a); + if (!R) + if (null !== J(L)) (R = !0), f(V); + else { + var b = J(M); + null !== b && g(U, b.startTime - a); + } + } + function V(a, b) { + R = !1; + S && ((S = !1), h()); + Q = !0; + var c = P; + try { + T(b); + for ( + O = J(L); + null !== O && + (!(O.expirationTime > b) || + (a && !exports.unstable_shouldYield())); + + ) { + var d = O.callback; + if ("function" === typeof d) { + O.callback = null; + P = O.priorityLevel; + var e = d(O.expirationTime <= b); + b = exports.unstable_now(); + "function" === typeof e + ? (O.callback = e) + : O === J(L) && K(L); + T(b); + } else K(L); + O = J(L); + } + if (null !== O) var m = !0; + else { + var n = J(M); + null !== n && g(U, n.startTime - b); + m = !1; + } + return m; + } finally { + (O = null), (P = c), (Q = !1); + } + } + var W = k; + exports.unstable_IdlePriority = 5; + exports.unstable_ImmediatePriority = 1; + exports.unstable_LowPriority = 4; + exports.unstable_NormalPriority = 3; + exports.unstable_Profiling = null; + exports.unstable_UserBlockingPriority = 2; + exports.unstable_cancelCallback = function (a) { + a.callback = null; + }; + exports.unstable_continueExecution = function () { + R || Q || ((R = !0), f(V)); + }; + exports.unstable_getCurrentPriorityLevel = function () { + return P; + }; + exports.unstable_getFirstCallbackNode = function () { + return J(L); + }; + exports.unstable_next = function (a) { + switch (P) { + case 1: + case 2: + case 3: + var b = 3; + break; + default: + b = P; + } + var c = P; + P = b; + try { + return a(); + } finally { + P = c; + } + }; + exports.unstable_pauseExecution = function () {}; + exports.unstable_requestPaint = W; + exports.unstable_runWithPriority = function (a, b) { + switch (a) { + case 1: + case 2: + case 3: + case 4: + case 5: + break; + default: + a = 3; + } + var c = P; + P = a; + try { + return b(); + } finally { + P = c; + } + }; + exports.unstable_scheduleCallback = function (a, b, c) { + var d = exports.unstable_now(); + "object" === typeof c && null !== c + ? ((c = c.delay), + (c = "number" === typeof c && 0 < c ? d + c : d)) + : (c = d); + switch (a) { + case 1: + var e = -1; + break; + case 2: + e = 250; + break; + case 5: + e = 1073741823; + break; + case 4: + e = 1e4; + break; + default: + e = 5e3; + } + e = c + e; + a = { + id: N++, + callback: b, + priorityLevel: a, + startTime: c, + expirationTime: e, + sortIndex: -1, + }; + c > d + ? ((a.sortIndex = c), + H(M, a), + null === J(L) && + a === J(M) && + (S ? h() : (S = !0), g(U, c - d))) + : ((a.sortIndex = e), H(L, a), R || Q || ((R = !0), f(V))); + return a; + }; + exports.unstable_wrapCallback = function (a) { + var b = P; + return function () { + var c = P; + P = b; + try { + return a.apply(this, arguments); + } finally { + P = c; + } + }; + }; + })(scheduler_production_min); + return scheduler_production_min; +} + +var hasRequiredScheduler; + +function requireScheduler() { + if (hasRequiredScheduler) return schedulerExports; + hasRequiredScheduler = 1; + (function (module) { + { + module.exports = requireScheduler_production_min(); + } + })(scheduler); + return schedulerExports; +} + +/** @license React v17.0.2 + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var hasRequiredReactDom_production_min; + +function requireReactDom_production_min() { + if (hasRequiredReactDom_production_min) return reactDom_production_min; + hasRequiredReactDom_production_min = 1; + var aa = reactExports, + m = requireObjectAssign(), + r = requireScheduler(); + function y(a) { + for ( + var b = + "https://reactjs.org/docs/error-decoder.html?invariant=" + + a, + c = 1; + c < arguments.length; + c++ + ) + b += "&args[]=" + encodeURIComponent(arguments[c]); + return ( + "Minified React error #" + + a + + "; visit " + + b + + " for the full message or use the non-minified dev environment for full errors and additional helpful warnings." + ); + } + if (!aa) throw Error(y(227)); + var ba = new Set(), + ca = {}; + function da(a, b) { + ea(a, b); + ea(a + "Capture", b); + } + function ea(a, b) { + ca[a] = b; + for (a = 0; a < b.length; a++) ba.add(b[a]); + } + var fa = !( + "undefined" === typeof window || + "undefined" === typeof window.document || + "undefined" === typeof window.document.createElement + ), + ha = + /^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, + ia = Object.prototype.hasOwnProperty, + ja = {}, + ka = {}; + function la(a) { + if (ia.call(ka, a)) return !0; + if (ia.call(ja, a)) return !1; + if (ha.test(a)) return (ka[a] = !0); + ja[a] = !0; + return !1; + } + function ma(a, b, c, d) { + if (null !== c && 0 === c.type) return !1; + switch (typeof b) { + case "function": + case "symbol": + return !0; + case "boolean": + if (d) return !1; + if (null !== c) return !c.acceptsBooleans; + a = a.toLowerCase().slice(0, 5); + return "data-" !== a && "aria-" !== a; + default: + return !1; + } + } + function na(a, b, c, d) { + if (null === b || "undefined" === typeof b || ma(a, b, c, d)) return !0; + if (d) return !1; + if (null !== c) + switch (c.type) { + case 3: + return !b; + case 4: + return !1 === b; + case 5: + return isNaN(b); + case 6: + return isNaN(b) || 1 > b; + } + return !1; + } + function B(a, b, c, d, e, f, g) { + this.acceptsBooleans = 2 === b || 3 === b || 4 === b; + this.attributeName = d; + this.attributeNamespace = e; + this.mustUseProperty = c; + this.propertyName = a; + this.type = b; + this.sanitizeURL = f; + this.removeEmptyString = g; + } + var D = {}; + "children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style" + .split(" ") + .forEach(function (a) { + D[a] = new B(a, 0, !1, a, null, !1, !1); + }); + [ + ["acceptCharset", "accept-charset"], + ["className", "class"], + ["htmlFor", "for"], + ["httpEquiv", "http-equiv"], + ].forEach(function (a) { + var b = a[0]; + D[b] = new B(b, 1, !1, a[1], null, !1, !1); + }); + ["contentEditable", "draggable", "spellCheck", "value"].forEach(function ( + a + ) { + D[a] = new B(a, 2, !1, a.toLowerCase(), null, !1, !1); + }); + [ + "autoReverse", + "externalResourcesRequired", + "focusable", + "preserveAlpha", + ].forEach(function (a) { + D[a] = new B(a, 2, !1, a, null, !1, !1); + }); + "allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope" + .split(" ") + .forEach(function (a) { + D[a] = new B(a, 3, !1, a.toLowerCase(), null, !1, !1); + }); + ["checked", "multiple", "muted", "selected"].forEach(function (a) { + D[a] = new B(a, 3, !0, a, null, !1, !1); + }); + ["capture", "download"].forEach(function (a) { + D[a] = new B(a, 4, !1, a, null, !1, !1); + }); + ["cols", "rows", "size", "span"].forEach(function (a) { + D[a] = new B(a, 6, !1, a, null, !1, !1); + }); + ["rowSpan", "start"].forEach(function (a) { + D[a] = new B(a, 5, !1, a.toLowerCase(), null, !1, !1); + }); + var oa = /[\-:]([a-z])/g; + function pa(a) { + return a[1].toUpperCase(); + } + "accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height" + .split(" ") + .forEach(function (a) { + var b = a.replace(oa, pa); + D[b] = new B(b, 1, !1, a, null, !1, !1); + }); + "xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type" + .split(" ") + .forEach(function (a) { + var b = a.replace(oa, pa); + D[b] = new B(b, 1, !1, a, "http://www.w3.org/1999/xlink", !1, !1); + }); + ["xml:base", "xml:lang", "xml:space"].forEach(function (a) { + var b = a.replace(oa, pa); + D[b] = new B( + b, + 1, + !1, + a, + "http://www.w3.org/XML/1998/namespace", + !1, + !1 + ); + }); + ["tabIndex", "crossOrigin"].forEach(function (a) { + D[a] = new B(a, 1, !1, a.toLowerCase(), null, !1, !1); + }); + D.xlinkHref = new B( + "xlinkHref", + 1, + !1, + "xlink:href", + "http://www.w3.org/1999/xlink", + !0, + !1 + ); + ["src", "href", "action", "formAction"].forEach(function (a) { + D[a] = new B(a, 1, !1, a.toLowerCase(), null, !0, !0); + }); + function qa(a, b, c, d) { + var e = D.hasOwnProperty(b) ? D[b] : null; + var f = + null !== e + ? 0 === e.type + : d + ? !1 + : !(2 < b.length) || + ("o" !== b[0] && "O" !== b[0]) || + ("n" !== b[1] && "N" !== b[1]) + ? !1 + : !0; + f || + (na(b, c, e, d) && (c = null), + d || null === e + ? la(b) && + (null === c + ? a.removeAttribute(b) + : a.setAttribute(b, "" + c)) + : e.mustUseProperty + ? (a[e.propertyName] = + null === c ? (3 === e.type ? !1 : "") : c) + : ((b = e.attributeName), + (d = e.attributeNamespace), + null === c + ? a.removeAttribute(b) + : ((e = e.type), + (c = 3 === e || (4 === e && !0 === c) ? "" : "" + c), + d + ? a.setAttributeNS(d, b, c) + : a.setAttribute(b, c)))); + } + var ra = aa.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + sa = 60103, + ta = 60106, + ua = 60107, + wa = 60108, + xa = 60114, + ya = 60109, + za = 60110, + Aa = 60112, + Ba = 60113, + Ca = 60120, + Da = 60115, + Ea = 60116, + Fa = 60121, + Ga = 60128, + Ha = 60129, + Ia = 60130, + Ja = 60131; + if ("function" === typeof Symbol && Symbol.for) { + var E = Symbol.for; + sa = E("react.element"); + ta = E("react.portal"); + ua = E("react.fragment"); + wa = E("react.strict_mode"); + xa = E("react.profiler"); + ya = E("react.provider"); + za = E("react.context"); + Aa = E("react.forward_ref"); + Ba = E("react.suspense"); + Ca = E("react.suspense_list"); + Da = E("react.memo"); + Ea = E("react.lazy"); + Fa = E("react.block"); + E("react.scope"); + Ga = E("react.opaque.id"); + Ha = E("react.debug_trace_mode"); + Ia = E("react.offscreen"); + Ja = E("react.legacy_hidden"); + } + var Ka = "function" === typeof Symbol && Symbol.iterator; + function La(a) { + if (null === a || "object" !== typeof a) return null; + a = (Ka && a[Ka]) || a["@@iterator"]; + return "function" === typeof a ? a : null; + } + var Ma; + function Na(a) { + if (void 0 === Ma) + try { + throw Error(); + } catch (c) { + var b = c.stack.trim().match(/\n( *(at )?)/); + Ma = (b && b[1]) || ""; + } + return "\n" + Ma + a; + } + var Oa = !1; + function Pa(a, b) { + if (!a || Oa) return ""; + Oa = !0; + var c = Error.prepareStackTrace; + Error.prepareStackTrace = void 0; + try { + if (b) + if ( + ((b = function () { + throw Error(); + }), + Object.defineProperty(b.prototype, "props", { + set: function () { + throw Error(); + }, + }), + "object" === typeof Reflect && Reflect.construct) + ) { + try { + Reflect.construct(b, []); + } catch (k) { + var d = k; + } + Reflect.construct(a, [], b); + } else { + try { + b.call(); + } catch (k) { + d = k; + } + a.call(b.prototype); + } + else { + try { + throw Error(); + } catch (k) { + d = k; + } + a(); + } + } catch (k) { + if (k && d && "string" === typeof k.stack) { + for ( + var e = k.stack.split("\n"), + f = d.stack.split("\n"), + g = e.length - 1, + h = f.length - 1; + 1 <= g && 0 <= h && e[g] !== f[h]; + + ) + h--; + for (; 1 <= g && 0 <= h; g--, h--) + if (e[g] !== f[h]) { + if (1 !== g || 1 !== h) { + do + if ((g--, h--, 0 > h || e[g] !== f[h])) + return ( + "\n" + e[g].replace(" at new ", " at ") + ); + while (1 <= g && 0 <= h); + } + break; + } + } + } finally { + (Oa = !1), (Error.prepareStackTrace = c); + } + return (a = a ? a.displayName || a.name : "") ? Na(a) : ""; + } + function Qa(a) { + switch (a.tag) { + case 5: + return Na(a.type); + case 16: + return Na("Lazy"); + case 13: + return Na("Suspense"); + case 19: + return Na("SuspenseList"); + case 0: + case 2: + case 15: + return (a = Pa(a.type, !1)), a; + case 11: + return (a = Pa(a.type.render, !1)), a; + case 22: + return (a = Pa(a.type._render, !1)), a; + case 1: + return (a = Pa(a.type, !0)), a; + default: + return ""; + } + } + function Ra(a) { + if (null == a) return null; + if ("function" === typeof a) return a.displayName || a.name || null; + if ("string" === typeof a) return a; + switch (a) { + case ua: + return "Fragment"; + case ta: + return "Portal"; + case xa: + return "Profiler"; + case wa: + return "StrictMode"; + case Ba: + return "Suspense"; + case Ca: + return "SuspenseList"; + } + if ("object" === typeof a) + switch (a.$$typeof) { + case za: + return (a.displayName || "Context") + ".Consumer"; + case ya: + return (a._context.displayName || "Context") + ".Provider"; + case Aa: + var b = a.render; + b = b.displayName || b.name || ""; + return ( + a.displayName || + ("" !== b ? "ForwardRef(" + b + ")" : "ForwardRef") + ); + case Da: + return Ra(a.type); + case Fa: + return Ra(a._render); + case Ea: + b = a._payload; + a = a._init; + try { + return Ra(a(b)); + } catch (c) {} + } + return null; + } + function Sa(a) { + switch (typeof a) { + case "boolean": + case "number": + case "object": + case "string": + case "undefined": + return a; + default: + return ""; + } + } + function Ta(a) { + var b = a.type; + return ( + (a = a.nodeName) && + "input" === a.toLowerCase() && + ("checkbox" === b || "radio" === b) + ); + } + function Ua(a) { + var b = Ta(a) ? "checked" : "value", + c = Object.getOwnPropertyDescriptor(a.constructor.prototype, b), + d = "" + a[b]; + if ( + !a.hasOwnProperty(b) && + "undefined" !== typeof c && + "function" === typeof c.get && + "function" === typeof c.set + ) { + var e = c.get, + f = c.set; + Object.defineProperty(a, b, { + configurable: !0, + get: function () { + return e.call(this); + }, + set: function (a) { + d = "" + a; + f.call(this, a); + }, + }); + Object.defineProperty(a, b, { enumerable: c.enumerable }); + return { + getValue: function () { + return d; + }, + setValue: function (a) { + d = "" + a; + }, + stopTracking: function () { + a._valueTracker = null; + delete a[b]; + }, + }; + } + } + function Va(a) { + a._valueTracker || (a._valueTracker = Ua(a)); + } + function Wa(a) { + if (!a) return !1; + var b = a._valueTracker; + if (!b) return !0; + var c = b.getValue(); + var d = ""; + a && (d = Ta(a) ? (a.checked ? "true" : "false") : a.value); + a = d; + return a !== c ? (b.setValue(a), !0) : !1; + } + function Xa(a) { + a = a || ("undefined" !== typeof document ? document : void 0); + if ("undefined" === typeof a) return null; + try { + return a.activeElement || a.body; + } catch (b) { + return a.body; + } + } + function Ya(a, b) { + var c = b.checked; + return m({}, b, { + defaultChecked: void 0, + defaultValue: void 0, + value: void 0, + checked: null != c ? c : a._wrapperState.initialChecked, + }); + } + function Za(a, b) { + var c = null == b.defaultValue ? "" : b.defaultValue, + d = null != b.checked ? b.checked : b.defaultChecked; + c = Sa(null != b.value ? b.value : c); + a._wrapperState = { + initialChecked: d, + initialValue: c, + controlled: + "checkbox" === b.type || "radio" === b.type + ? null != b.checked + : null != b.value, + }; + } + function $a(a, b) { + b = b.checked; + null != b && qa(a, "checked", b, !1); + } + function ab(a, b) { + $a(a, b); + var c = Sa(b.value), + d = b.type; + if (null != c) + if ("number" === d) { + if ((0 === c && "" === a.value) || a.value != c) + a.value = "" + c; + } else a.value !== "" + c && (a.value = "" + c); + else if ("submit" === d || "reset" === d) { + a.removeAttribute("value"); + return; + } + b.hasOwnProperty("value") + ? bb(a, b.type, c) + : b.hasOwnProperty("defaultValue") && + bb(a, b.type, Sa(b.defaultValue)); + null == b.checked && + null != b.defaultChecked && + (a.defaultChecked = !!b.defaultChecked); + } + function cb(a, b, c) { + if (b.hasOwnProperty("value") || b.hasOwnProperty("defaultValue")) { + var d = b.type; + if ( + !( + ("submit" !== d && "reset" !== d) || + (void 0 !== b.value && null !== b.value) + ) + ) + return; + b = "" + a._wrapperState.initialValue; + c || b === a.value || (a.value = b); + a.defaultValue = b; + } + c = a.name; + "" !== c && (a.name = ""); + a.defaultChecked = !!a._wrapperState.initialChecked; + "" !== c && (a.name = c); + } + function bb(a, b, c) { + if ("number" !== b || Xa(a.ownerDocument) !== a) + null == c + ? (a.defaultValue = "" + a._wrapperState.initialValue) + : a.defaultValue !== "" + c && (a.defaultValue = "" + c); + } + function db(a) { + var b = ""; + aa.Children.forEach(a, function (a) { + null != a && (b += a); + }); + return b; + } + function eb(a, b) { + a = m({ children: void 0 }, b); + if ((b = db(b.children))) a.children = b; + return a; + } + function fb(a, b, c, d) { + a = a.options; + if (b) { + b = {}; + for (var e = 0; e < c.length; e++) b["$" + c[e]] = !0; + for (c = 0; c < a.length; c++) + (e = b.hasOwnProperty("$" + a[c].value)), + a[c].selected !== e && (a[c].selected = e), + e && d && (a[c].defaultSelected = !0); + } else { + c = "" + Sa(c); + b = null; + for (e = 0; e < a.length; e++) { + if (a[e].value === c) { + a[e].selected = !0; + d && (a[e].defaultSelected = !0); + return; + } + null !== b || a[e].disabled || (b = a[e]); + } + null !== b && (b.selected = !0); + } + } + function gb(a, b) { + if (null != b.dangerouslySetInnerHTML) throw Error(y(91)); + return m({}, b, { + value: void 0, + defaultValue: void 0, + children: "" + a._wrapperState.initialValue, + }); + } + function hb(a, b) { + var c = b.value; + if (null == c) { + c = b.children; + b = b.defaultValue; + if (null != c) { + if (null != b) throw Error(y(92)); + if (Array.isArray(c)) { + if (!(1 >= c.length)) throw Error(y(93)); + c = c[0]; + } + b = c; + } + null == b && (b = ""); + c = b; + } + a._wrapperState = { initialValue: Sa(c) }; + } + function ib(a, b) { + var c = Sa(b.value), + d = Sa(b.defaultValue); + null != c && + ((c = "" + c), + c !== a.value && (a.value = c), + null == b.defaultValue && + a.defaultValue !== c && + (a.defaultValue = c)); + null != d && (a.defaultValue = "" + d); + } + function jb(a) { + var b = a.textContent; + b === a._wrapperState.initialValue && + "" !== b && + null !== b && + (a.value = b); + } + var kb = { + html: "http://www.w3.org/1999/xhtml", + mathml: "http://www.w3.org/1998/Math/MathML", + svg: "http://www.w3.org/2000/svg", + }; + function lb(a) { + switch (a) { + case "svg": + return "http://www.w3.org/2000/svg"; + case "math": + return "http://www.w3.org/1998/Math/MathML"; + default: + return "http://www.w3.org/1999/xhtml"; + } + } + function mb(a, b) { + return null == a || "http://www.w3.org/1999/xhtml" === a + ? lb(b) + : "http://www.w3.org/2000/svg" === a && "foreignObject" === b + ? "http://www.w3.org/1999/xhtml" + : a; + } + var nb, + ob = (function (a) { + return "undefined" !== typeof MSApp && MSApp.execUnsafeLocalFunction + ? function (b, c, d, e) { + MSApp.execUnsafeLocalFunction(function () { + return a(b, c, d, e); + }); + } + : a; + })(function (a, b) { + if (a.namespaceURI !== kb.svg || "innerHTML" in a) a.innerHTML = b; + else { + nb = nb || document.createElement("div"); + nb.innerHTML = "" + b.valueOf().toString() + ""; + for (b = nb.firstChild; a.firstChild; ) + a.removeChild(a.firstChild); + for (; b.firstChild; ) a.appendChild(b.firstChild); + } + }); + function pb(a, b) { + if (b) { + var c = a.firstChild; + if (c && c === a.lastChild && 3 === c.nodeType) { + c.nodeValue = b; + return; + } + } + a.textContent = b; + } + var qb = { + animationIterationCount: !0, + borderImageOutset: !0, + borderImageSlice: !0, + borderImageWidth: !0, + boxFlex: !0, + boxFlexGroup: !0, + boxOrdinalGroup: !0, + columnCount: !0, + columns: !0, + flex: !0, + flexGrow: !0, + flexPositive: !0, + flexShrink: !0, + flexNegative: !0, + flexOrder: !0, + gridArea: !0, + gridRow: !0, + gridRowEnd: !0, + gridRowSpan: !0, + gridRowStart: !0, + gridColumn: !0, + gridColumnEnd: !0, + gridColumnSpan: !0, + gridColumnStart: !0, + fontWeight: !0, + lineClamp: !0, + lineHeight: !0, + opacity: !0, + order: !0, + orphans: !0, + tabSize: !0, + widows: !0, + zIndex: !0, + zoom: !0, + fillOpacity: !0, + floodOpacity: !0, + stopOpacity: !0, + strokeDasharray: !0, + strokeDashoffset: !0, + strokeMiterlimit: !0, + strokeOpacity: !0, + strokeWidth: !0, + }, + rb = ["Webkit", "ms", "Moz", "O"]; + Object.keys(qb).forEach(function (a) { + rb.forEach(function (b) { + b = b + a.charAt(0).toUpperCase() + a.substring(1); + qb[b] = qb[a]; + }); + }); + function sb(a, b, c) { + return null == b || "boolean" === typeof b || "" === b + ? "" + : c || + "number" !== typeof b || + 0 === b || + (qb.hasOwnProperty(a) && qb[a]) + ? ("" + b).trim() + : b + "px"; + } + function tb(a, b) { + a = a.style; + for (var c in b) + if (b.hasOwnProperty(c)) { + var d = 0 === c.indexOf("--"), + e = sb(c, b[c], d); + "float" === c && (c = "cssFloat"); + d ? a.setProperty(c, e) : (a[c] = e); + } + } + var ub = m( + { menuitem: !0 }, + { + area: !0, + base: !0, + br: !0, + col: !0, + embed: !0, + hr: !0, + img: !0, + input: !0, + keygen: !0, + link: !0, + meta: !0, + param: !0, + source: !0, + track: !0, + wbr: !0, + } + ); + function vb(a, b) { + if (b) { + if ( + ub[a] && + (null != b.children || null != b.dangerouslySetInnerHTML) + ) + throw Error(y(137, a)); + if (null != b.dangerouslySetInnerHTML) { + if (null != b.children) throw Error(y(60)); + if ( + !( + "object" === typeof b.dangerouslySetInnerHTML && + "__html" in b.dangerouslySetInnerHTML + ) + ) + throw Error(y(61)); + } + if (null != b.style && "object" !== typeof b.style) + throw Error(y(62)); + } + } + function wb(a, b) { + if (-1 === a.indexOf("-")) return "string" === typeof b.is; + switch (a) { + case "annotation-xml": + case "color-profile": + case "font-face": + case "font-face-src": + case "font-face-uri": + case "font-face-format": + case "font-face-name": + case "missing-glyph": + return !1; + default: + return !0; + } + } + function xb(a) { + a = a.target || a.srcElement || window; + a.correspondingUseElement && (a = a.correspondingUseElement); + return 3 === a.nodeType ? a.parentNode : a; + } + var yb = null, + zb = null, + Ab = null; + function Bb(a) { + if ((a = Cb(a))) { + if ("function" !== typeof yb) throw Error(y(280)); + var b = a.stateNode; + b && ((b = Db(b)), yb(a.stateNode, a.type, b)); + } + } + function Eb(a) { + zb ? (Ab ? Ab.push(a) : (Ab = [a])) : (zb = a); + } + function Fb() { + if (zb) { + var a = zb, + b = Ab; + Ab = zb = null; + Bb(a); + if (b) for (a = 0; a < b.length; a++) Bb(b[a]); + } + } + function Gb(a, b) { + return a(b); + } + function Hb(a, b, c, d, e) { + return a(b, c, d, e); + } + function Ib() {} + var Jb = Gb, + Kb = !1, + Lb = !1; + function Mb() { + if (null !== zb || null !== Ab) Ib(), Fb(); + } + function Nb(a, b, c) { + if (Lb) return a(b, c); + Lb = !0; + try { + return Jb(a, b, c); + } finally { + (Lb = !1), Mb(); + } + } + function Ob(a, b) { + var c = a.stateNode; + if (null === c) return null; + var d = Db(c); + if (null === d) return null; + c = d[b]; + a: switch (b) { + case "onClick": + case "onClickCapture": + case "onDoubleClick": + case "onDoubleClickCapture": + case "onMouseDown": + case "onMouseDownCapture": + case "onMouseMove": + case "onMouseMoveCapture": + case "onMouseUp": + case "onMouseUpCapture": + case "onMouseEnter": + (d = !d.disabled) || + ((a = a.type), + (d = !( + "button" === a || + "input" === a || + "select" === a || + "textarea" === a + ))); + a = !d; + break a; + default: + a = !1; + } + if (a) return null; + if (c && "function" !== typeof c) throw Error(y(231, b, typeof c)); + return c; + } + var Pb = !1; + if (fa) + try { + var Qb = {}; + Object.defineProperty(Qb, "passive", { + get: function () { + Pb = !0; + }, + }); + window.addEventListener("test", Qb, Qb); + window.removeEventListener("test", Qb, Qb); + } catch (a) { + Pb = !1; + } + function Rb(a, b, c, d, e, f, g, h, k) { + var l = Array.prototype.slice.call(arguments, 3); + try { + b.apply(c, l); + } catch (n) { + this.onError(n); + } + } + var Sb = !1, + Tb = null, + Ub = !1, + Vb = null, + Wb = { + onError: function (a) { + Sb = !0; + Tb = a; + }, + }; + function Xb(a, b, c, d, e, f, g, h, k) { + Sb = !1; + Tb = null; + Rb.apply(Wb, arguments); + } + function Yb(a, b, c, d, e, f, g, h, k) { + Xb.apply(this, arguments); + if (Sb) { + if (Sb) { + var l = Tb; + Sb = !1; + Tb = null; + } else throw Error(y(198)); + Ub || ((Ub = !0), (Vb = l)); + } + } + function Zb(a) { + var b = a, + c = a; + if (a.alternate) for (; b.return; ) b = b.return; + else { + a = b; + do + (b = a), + 0 !== (b.flags & 1026) && (c = b.return), + (a = b.return); + while (a); + } + return 3 === b.tag ? c : null; + } + function $b(a) { + if (13 === a.tag) { + var b = a.memoizedState; + null === b && + ((a = a.alternate), null !== a && (b = a.memoizedState)); + if (null !== b) return b.dehydrated; + } + return null; + } + function ac(a) { + if (Zb(a) !== a) throw Error(y(188)); + } + function bc(a) { + var b = a.alternate; + if (!b) { + b = Zb(a); + if (null === b) throw Error(y(188)); + return b !== a ? null : a; + } + for (var c = a, d = b; ; ) { + var e = c.return; + if (null === e) break; + var f = e.alternate; + if (null === f) { + d = e.return; + if (null !== d) { + c = d; + continue; + } + break; + } + if (e.child === f.child) { + for (f = e.child; f; ) { + if (f === c) return ac(e), a; + if (f === d) return ac(e), b; + f = f.sibling; + } + throw Error(y(188)); + } + if (c.return !== d.return) (c = e), (d = f); + else { + for (var g = !1, h = e.child; h; ) { + if (h === c) { + g = !0; + c = e; + d = f; + break; + } + if (h === d) { + g = !0; + d = e; + c = f; + break; + } + h = h.sibling; + } + if (!g) { + for (h = f.child; h; ) { + if (h === c) { + g = !0; + c = f; + d = e; + break; + } + if (h === d) { + g = !0; + d = f; + c = e; + break; + } + h = h.sibling; + } + if (!g) throw Error(y(189)); + } + } + if (c.alternate !== d) throw Error(y(190)); + } + if (3 !== c.tag) throw Error(y(188)); + return c.stateNode.current === c ? a : b; + } + function cc(a) { + a = bc(a); + if (!a) return null; + for (var b = a; ; ) { + if (5 === b.tag || 6 === b.tag) return b; + if (b.child) (b.child.return = b), (b = b.child); + else { + if (b === a) break; + for (; !b.sibling; ) { + if (!b.return || b.return === a) return null; + b = b.return; + } + b.sibling.return = b.return; + b = b.sibling; + } + } + return null; + } + function dc(a, b) { + for (var c = a.alternate; null !== b; ) { + if (b === a || b === c) return !0; + b = b.return; + } + return !1; + } + var ec, + fc, + gc, + hc, + ic = !1, + jc = [], + kc = null, + lc = null, + mc = null, + nc = new Map(), + oc = new Map(), + pc = [], + qc = + "mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit".split( + " " + ); + function rc(a, b, c, d, e) { + return { + blockedOn: a, + domEventName: b, + eventSystemFlags: c | 16, + nativeEvent: e, + targetContainers: [d], + }; + } + function sc(a, b) { + switch (a) { + case "focusin": + case "focusout": + kc = null; + break; + case "dragenter": + case "dragleave": + lc = null; + break; + case "mouseover": + case "mouseout": + mc = null; + break; + case "pointerover": + case "pointerout": + nc.delete(b.pointerId); + break; + case "gotpointercapture": + case "lostpointercapture": + oc.delete(b.pointerId); + } + } + function tc(a, b, c, d, e, f) { + if (null === a || a.nativeEvent !== f) + return ( + (a = rc(b, c, d, e, f)), + null !== b && ((b = Cb(b)), null !== b && fc(b)), + a + ); + a.eventSystemFlags |= d; + b = a.targetContainers; + null !== e && -1 === b.indexOf(e) && b.push(e); + return a; + } + function uc(a, b, c, d, e) { + switch (b) { + case "focusin": + return (kc = tc(kc, a, b, c, d, e)), !0; + case "dragenter": + return (lc = tc(lc, a, b, c, d, e)), !0; + case "mouseover": + return (mc = tc(mc, a, b, c, d, e)), !0; + case "pointerover": + var f = e.pointerId; + nc.set(f, tc(nc.get(f) || null, a, b, c, d, e)); + return !0; + case "gotpointercapture": + return ( + (f = e.pointerId), + oc.set(f, tc(oc.get(f) || null, a, b, c, d, e)), + !0 + ); + } + return !1; + } + function vc(a) { + var b = wc(a.target); + if (null !== b) { + var c = Zb(b); + if (null !== c) + if (((b = c.tag), 13 === b)) { + if (((b = $b(c)), null !== b)) { + a.blockedOn = b; + hc(a.lanePriority, function () { + r.unstable_runWithPriority(a.priority, function () { + gc(c); + }); + }); + return; + } + } else if (3 === b && c.stateNode.hydrate) { + a.blockedOn = + 3 === c.tag ? c.stateNode.containerInfo : null; + return; + } + } + a.blockedOn = null; + } + function xc(a) { + if (null !== a.blockedOn) return !1; + for (var b = a.targetContainers; 0 < b.length; ) { + var c = yc(a.domEventName, a.eventSystemFlags, b[0], a.nativeEvent); + if (null !== c) + return (b = Cb(c)), null !== b && fc(b), (a.blockedOn = c), !1; + b.shift(); + } + return !0; + } + function zc(a, b, c) { + xc(a) && c.delete(b); + } + function Ac() { + for (ic = !1; 0 < jc.length; ) { + var a = jc[0]; + if (null !== a.blockedOn) { + a = Cb(a.blockedOn); + null !== a && ec(a); + break; + } + for (var b = a.targetContainers; 0 < b.length; ) { + var c = yc( + a.domEventName, + a.eventSystemFlags, + b[0], + a.nativeEvent + ); + if (null !== c) { + a.blockedOn = c; + break; + } + b.shift(); + } + null === a.blockedOn && jc.shift(); + } + null !== kc && xc(kc) && (kc = null); + null !== lc && xc(lc) && (lc = null); + null !== mc && xc(mc) && (mc = null); + nc.forEach(zc); + oc.forEach(zc); + } + function Bc(a, b) { + a.blockedOn === b && + ((a.blockedOn = null), + ic || + ((ic = !0), + r.unstable_scheduleCallback(r.unstable_NormalPriority, Ac))); + } + function Cc(a) { + function b(b) { + return Bc(b, a); + } + if (0 < jc.length) { + Bc(jc[0], a); + for (var c = 1; c < jc.length; c++) { + var d = jc[c]; + d.blockedOn === a && (d.blockedOn = null); + } + } + null !== kc && Bc(kc, a); + null !== lc && Bc(lc, a); + null !== mc && Bc(mc, a); + nc.forEach(b); + oc.forEach(b); + for (c = 0; c < pc.length; c++) + (d = pc[c]), d.blockedOn === a && (d.blockedOn = null); + for (; 0 < pc.length && ((c = pc[0]), null === c.blockedOn); ) + vc(c), null === c.blockedOn && pc.shift(); + } + function Dc(a, b) { + var c = {}; + c[a.toLowerCase()] = b.toLowerCase(); + c["Webkit" + a] = "webkit" + b; + c["Moz" + a] = "moz" + b; + return c; + } + var Ec = { + animationend: Dc("Animation", "AnimationEnd"), + animationiteration: Dc("Animation", "AnimationIteration"), + animationstart: Dc("Animation", "AnimationStart"), + transitionend: Dc("Transition", "TransitionEnd"), + }, + Fc = {}, + Gc = {}; + fa && + ((Gc = document.createElement("div").style), + "AnimationEvent" in window || + (delete Ec.animationend.animation, + delete Ec.animationiteration.animation, + delete Ec.animationstart.animation), + "TransitionEvent" in window || delete Ec.transitionend.transition); + function Hc(a) { + if (Fc[a]) return Fc[a]; + if (!Ec[a]) return a; + var b = Ec[a], + c; + for (c in b) if (b.hasOwnProperty(c) && c in Gc) return (Fc[a] = b[c]); + return a; + } + var Ic = Hc("animationend"), + Jc = Hc("animationiteration"), + Kc = Hc("animationstart"), + Lc = Hc("transitionend"), + Mc = new Map(), + Nc = new Map(), + Oc = [ + "abort", + "abort", + Ic, + "animationEnd", + Jc, + "animationIteration", + Kc, + "animationStart", + "canplay", + "canPlay", + "canplaythrough", + "canPlayThrough", + "durationchange", + "durationChange", + "emptied", + "emptied", + "encrypted", + "encrypted", + "ended", + "ended", + "error", + "error", + "gotpointercapture", + "gotPointerCapture", + "load", + "load", + "loadeddata", + "loadedData", + "loadedmetadata", + "loadedMetadata", + "loadstart", + "loadStart", + "lostpointercapture", + "lostPointerCapture", + "playing", + "playing", + "progress", + "progress", + "seeking", + "seeking", + "stalled", + "stalled", + "suspend", + "suspend", + "timeupdate", + "timeUpdate", + Lc, + "transitionEnd", + "waiting", + "waiting", + ]; + function Pc(a, b) { + for (var c = 0; c < a.length; c += 2) { + var d = a[c], + e = a[c + 1]; + e = "on" + (e[0].toUpperCase() + e.slice(1)); + Nc.set(d, b); + Mc.set(d, e); + da(e, [d]); + } + } + var Qc = r.unstable_now; + Qc(); + var F = 8; + function Rc(a) { + if (0 !== (1 & a)) return (F = 15), 1; + if (0 !== (2 & a)) return (F = 14), 2; + if (0 !== (4 & a)) return (F = 13), 4; + var b = 24 & a; + if (0 !== b) return (F = 12), b; + if (0 !== (a & 32)) return (F = 11), 32; + b = 192 & a; + if (0 !== b) return (F = 10), b; + if (0 !== (a & 256)) return (F = 9), 256; + b = 3584 & a; + if (0 !== b) return (F = 8), b; + if (0 !== (a & 4096)) return (F = 7), 4096; + b = 4186112 & a; + if (0 !== b) return (F = 6), b; + b = 62914560 & a; + if (0 !== b) return (F = 5), b; + if (a & 67108864) return (F = 4), 67108864; + if (0 !== (a & 134217728)) return (F = 3), 134217728; + b = 805306368 & a; + if (0 !== b) return (F = 2), b; + if (0 !== (1073741824 & a)) return (F = 1), 1073741824; + F = 8; + return a; + } + function Sc(a) { + switch (a) { + case 99: + return 15; + case 98: + return 10; + case 97: + case 96: + return 8; + case 95: + return 2; + default: + return 0; + } + } + function Tc(a) { + switch (a) { + case 15: + case 14: + return 99; + case 13: + case 12: + case 11: + case 10: + return 98; + case 9: + case 8: + case 7: + case 6: + case 4: + case 5: + return 97; + case 3: + case 2: + case 1: + return 95; + case 0: + return 90; + default: + throw Error(y(358, a)); + } + } + function Uc(a, b) { + var c = a.pendingLanes; + if (0 === c) return (F = 0); + var d = 0, + e = 0, + f = a.expiredLanes, + g = a.suspendedLanes, + h = a.pingedLanes; + if (0 !== f) (d = f), (e = F = 15); + else if (((f = c & 134217727), 0 !== f)) { + var k = f & ~g; + 0 !== k + ? ((d = Rc(k)), (e = F)) + : ((h &= f), 0 !== h && ((d = Rc(h)), (e = F))); + } else + (f = c & ~g), + 0 !== f + ? ((d = Rc(f)), (e = F)) + : 0 !== h && ((d = Rc(h)), (e = F)); + if (0 === d) return 0; + d = 31 - Vc(d); + d = c & (((0 > d ? 0 : 1 << d) << 1) - 1); + if (0 !== b && b !== d && 0 === (b & g)) { + Rc(b); + if (e <= F) return b; + F = e; + } + b = a.entangledLanes; + if (0 !== b) + for (a = a.entanglements, b &= d; 0 < b; ) + (c = 31 - Vc(b)), (e = 1 << c), (d |= a[c]), (b &= ~e); + return d; + } + function Wc(a) { + a = a.pendingLanes & -1073741825; + return 0 !== a ? a : a & 1073741824 ? 1073741824 : 0; + } + function Xc(a, b) { + switch (a) { + case 15: + return 1; + case 14: + return 2; + case 12: + return (a = Yc(24 & ~b)), 0 === a ? Xc(10, b) : a; + case 10: + return (a = Yc(192 & ~b)), 0 === a ? Xc(8, b) : a; + case 8: + return ( + (a = Yc(3584 & ~b)), + 0 === a && ((a = Yc(4186112 & ~b)), 0 === a && (a = 512)), + a + ); + case 2: + return (b = Yc(805306368 & ~b)), 0 === b && (b = 268435456), b; + } + throw Error(y(358, a)); + } + function Yc(a) { + return a & -a; + } + function Zc(a) { + for (var b = [], c = 0; 31 > c; c++) b.push(a); + return b; + } + function $c(a, b, c) { + a.pendingLanes |= b; + var d = b - 1; + a.suspendedLanes &= d; + a.pingedLanes &= d; + a = a.eventTimes; + b = 31 - Vc(b); + a[b] = c; + } + var Vc = Math.clz32 ? Math.clz32 : ad, + bd = Math.log, + cd = Math.LN2; + function ad(a) { + return 0 === a ? 32 : (31 - ((bd(a) / cd) | 0)) | 0; + } + var dd = r.unstable_UserBlockingPriority, + ed = r.unstable_runWithPriority, + fd = !0; + function gd(a, b, c, d) { + Kb || Ib(); + var e = hd, + f = Kb; + Kb = !0; + try { + Hb(e, a, b, c, d); + } finally { + (Kb = f) || Mb(); + } + } + function id(a, b, c, d) { + ed(dd, hd.bind(null, a, b, c, d)); + } + function hd(a, b, c, d) { + if (fd) { + var e; + if ((e = 0 === (b & 4)) && 0 < jc.length && -1 < qc.indexOf(a)) + (a = rc(null, a, b, c, d)), jc.push(a); + else { + var f = yc(a, b, c, d); + if (null === f) e && sc(a, d); + else { + if (e) { + if (-1 < qc.indexOf(a)) { + a = rc(f, a, b, c, d); + jc.push(a); + return; + } + if (uc(f, a, b, c, d)) return; + sc(a, d); + } + jd(a, b, d, null, c); + } + } + } + } + function yc(a, b, c, d) { + var e = xb(d); + e = wc(e); + if (null !== e) { + var f = Zb(e); + if (null === f) e = null; + else { + var g = f.tag; + if (13 === g) { + e = $b(f); + if (null !== e) return e; + e = null; + } else if (3 === g) { + if (f.stateNode.hydrate) + return 3 === f.tag ? f.stateNode.containerInfo : null; + e = null; + } else f !== e && (e = null); + } + } + jd(a, b, d, e, c); + return null; + } + var kd = null, + ld = null, + md = null; + function nd() { + if (md) return md; + var a, + b = ld, + c = b.length, + d, + e = "value" in kd ? kd.value : kd.textContent, + f = e.length; + for (a = 0; a < c && b[a] === e[a]; a++); + var g = c - a; + for (d = 1; d <= g && b[c - d] === e[f - d]; d++); + return (md = e.slice(a, 1 < d ? 1 - d : void 0)); + } + function od(a) { + var b = a.keyCode; + "charCode" in a + ? ((a = a.charCode), 0 === a && 13 === b && (a = 13)) + : (a = b); + 10 === a && (a = 13); + return 32 <= a || 13 === a ? a : 0; + } + function pd() { + return !0; + } + function qd() { + return !1; + } + function rd(a) { + function b(b, d, e, f, g) { + this._reactName = b; + this._targetInst = e; + this.type = d; + this.nativeEvent = f; + this.target = g; + this.currentTarget = null; + for (var c in a) + a.hasOwnProperty(c) && + ((b = a[c]), (this[c] = b ? b(f) : f[c])); + this.isDefaultPrevented = ( + null != f.defaultPrevented + ? f.defaultPrevented + : !1 === f.returnValue + ) + ? pd + : qd; + this.isPropagationStopped = qd; + return this; + } + m(b.prototype, { + preventDefault: function () { + this.defaultPrevented = !0; + var a = this.nativeEvent; + a && + (a.preventDefault + ? a.preventDefault() + : "unknown" !== typeof a.returnValue && + (a.returnValue = !1), + (this.isDefaultPrevented = pd)); + }, + stopPropagation: function () { + var a = this.nativeEvent; + a && + (a.stopPropagation + ? a.stopPropagation() + : "unknown" !== typeof a.cancelBubble && + (a.cancelBubble = !0), + (this.isPropagationStopped = pd)); + }, + persist: function () {}, + isPersistent: pd, + }); + return b; + } + var sd = { + eventPhase: 0, + bubbles: 0, + cancelable: 0, + timeStamp: function (a) { + return a.timeStamp || Date.now(); + }, + defaultPrevented: 0, + isTrusted: 0, + }, + td = rd(sd), + ud = m({}, sd, { view: 0, detail: 0 }), + vd = rd(ud), + wd, + xd, + yd, + Ad = m({}, ud, { + screenX: 0, + screenY: 0, + clientX: 0, + clientY: 0, + pageX: 0, + pageY: 0, + ctrlKey: 0, + shiftKey: 0, + altKey: 0, + metaKey: 0, + getModifierState: zd, + button: 0, + buttons: 0, + relatedTarget: function (a) { + return void 0 === a.relatedTarget + ? a.fromElement === a.srcElement + ? a.toElement + : a.fromElement + : a.relatedTarget; + }, + movementX: function (a) { + if ("movementX" in a) return a.movementX; + a !== yd && + (yd && "mousemove" === a.type + ? ((wd = a.screenX - yd.screenX), + (xd = a.screenY - yd.screenY)) + : (xd = wd = 0), + (yd = a)); + return wd; + }, + movementY: function (a) { + return "movementY" in a ? a.movementY : xd; + }, + }), + Bd = rd(Ad), + Cd = m({}, Ad, { dataTransfer: 0 }), + Dd = rd(Cd), + Ed = m({}, ud, { relatedTarget: 0 }), + Fd = rd(Ed), + Gd = m({}, sd, { animationName: 0, elapsedTime: 0, pseudoElement: 0 }), + Hd = rd(Gd), + Id = m({}, sd, { + clipboardData: function (a) { + return "clipboardData" in a + ? a.clipboardData + : window.clipboardData; + }, + }), + Jd = rd(Id), + Kd = m({}, sd, { data: 0 }), + Ld = rd(Kd), + Md = { + Esc: "Escape", + Spacebar: " ", + Left: "ArrowLeft", + Up: "ArrowUp", + Right: "ArrowRight", + Down: "ArrowDown", + Del: "Delete", + Win: "OS", + Menu: "ContextMenu", + Apps: "ContextMenu", + Scroll: "ScrollLock", + MozPrintableKey: "Unidentified", + }, + Nd = { + 8: "Backspace", + 9: "Tab", + 12: "Clear", + 13: "Enter", + 16: "Shift", + 17: "Control", + 18: "Alt", + 19: "Pause", + 20: "CapsLock", + 27: "Escape", + 32: " ", + 33: "PageUp", + 34: "PageDown", + 35: "End", + 36: "Home", + 37: "ArrowLeft", + 38: "ArrowUp", + 39: "ArrowRight", + 40: "ArrowDown", + 45: "Insert", + 46: "Delete", + 112: "F1", + 113: "F2", + 114: "F3", + 115: "F4", + 116: "F5", + 117: "F6", + 118: "F7", + 119: "F8", + 120: "F9", + 121: "F10", + 122: "F11", + 123: "F12", + 144: "NumLock", + 145: "ScrollLock", + 224: "Meta", + }, + Od = { + Alt: "altKey", + Control: "ctrlKey", + Meta: "metaKey", + Shift: "shiftKey", + }; + function Pd(a) { + var b = this.nativeEvent; + return b.getModifierState + ? b.getModifierState(a) + : (a = Od[a]) + ? !!b[a] + : !1; + } + function zd() { + return Pd; + } + var Qd = m({}, ud, { + key: function (a) { + if (a.key) { + var b = Md[a.key] || a.key; + if ("Unidentified" !== b) return b; + } + return "keypress" === a.type + ? ((a = od(a)), 13 === a ? "Enter" : String.fromCharCode(a)) + : "keydown" === a.type || "keyup" === a.type + ? Nd[a.keyCode] || "Unidentified" + : ""; + }, + code: 0, + location: 0, + ctrlKey: 0, + shiftKey: 0, + altKey: 0, + metaKey: 0, + repeat: 0, + locale: 0, + getModifierState: zd, + charCode: function (a) { + return "keypress" === a.type ? od(a) : 0; + }, + keyCode: function (a) { + return "keydown" === a.type || "keyup" === a.type + ? a.keyCode + : 0; + }, + which: function (a) { + return "keypress" === a.type + ? od(a) + : "keydown" === a.type || "keyup" === a.type + ? a.keyCode + : 0; + }, + }), + Rd = rd(Qd), + Sd = m({}, Ad, { + pointerId: 0, + width: 0, + height: 0, + pressure: 0, + tangentialPressure: 0, + tiltX: 0, + tiltY: 0, + twist: 0, + pointerType: 0, + isPrimary: 0, + }), + Td = rd(Sd), + Ud = m({}, ud, { + touches: 0, + targetTouches: 0, + changedTouches: 0, + altKey: 0, + metaKey: 0, + ctrlKey: 0, + shiftKey: 0, + getModifierState: zd, + }), + Vd = rd(Ud), + Wd = m({}, sd, { propertyName: 0, elapsedTime: 0, pseudoElement: 0 }), + Xd = rd(Wd), + Yd = m({}, Ad, { + deltaX: function (a) { + return "deltaX" in a + ? a.deltaX + : "wheelDeltaX" in a + ? -a.wheelDeltaX + : 0; + }, + deltaY: function (a) { + return "deltaY" in a + ? a.deltaY + : "wheelDeltaY" in a + ? -a.wheelDeltaY + : "wheelDelta" in a + ? -a.wheelDelta + : 0; + }, + deltaZ: 0, + deltaMode: 0, + }), + Zd = rd(Yd), + $d = [9, 13, 27, 32], + ae = fa && "CompositionEvent" in window, + be = null; + fa && "documentMode" in document && (be = document.documentMode); + var ce = fa && "TextEvent" in window && !be, + de = fa && (!ae || (be && 8 < be && 11 >= be)), + ee = String.fromCharCode(32), + fe = !1; + function ge(a, b) { + switch (a) { + case "keyup": + return -1 !== $d.indexOf(b.keyCode); + case "keydown": + return 229 !== b.keyCode; + case "keypress": + case "mousedown": + case "focusout": + return !0; + default: + return !1; + } + } + function he(a) { + a = a.detail; + return "object" === typeof a && "data" in a ? a.data : null; + } + var ie = !1; + function je(a, b) { + switch (a) { + case "compositionend": + return he(b); + case "keypress": + if (32 !== b.which) return null; + fe = !0; + return ee; + case "textInput": + return (a = b.data), a === ee && fe ? null : a; + default: + return null; + } + } + function ke(a, b) { + if (ie) + return "compositionend" === a || (!ae && ge(a, b)) + ? ((a = nd()), (md = ld = kd = null), (ie = !1), a) + : null; + switch (a) { + case "paste": + return null; + case "keypress": + if ( + !(b.ctrlKey || b.altKey || b.metaKey) || + (b.ctrlKey && b.altKey) + ) { + if (b.char && 1 < b.char.length) return b.char; + if (b.which) return String.fromCharCode(b.which); + } + return null; + case "compositionend": + return de && "ko" !== b.locale ? null : b.data; + default: + return null; + } + } + var le = { + color: !0, + date: !0, + datetime: !0, + "datetime-local": !0, + email: !0, + month: !0, + number: !0, + password: !0, + range: !0, + search: !0, + tel: !0, + text: !0, + time: !0, + url: !0, + week: !0, + }; + function me(a) { + var b = a && a.nodeName && a.nodeName.toLowerCase(); + return "input" === b ? !!le[a.type] : "textarea" === b ? !0 : !1; + } + function ne(a, b, c, d) { + Eb(d); + b = oe(b, "onChange"); + 0 < b.length && + ((c = new td("onChange", "change", null, c, d)), + a.push({ event: c, listeners: b })); + } + var pe = null, + qe = null; + function re(a) { + se(a, 0); + } + function te(a) { + var b = ue(a); + if (Wa(b)) return a; + } + function ve(a, b) { + if ("change" === a) return b; + } + var we = !1; + if (fa) { + var xe; + if (fa) { + var ye = "oninput" in document; + if (!ye) { + var ze = document.createElement("div"); + ze.setAttribute("oninput", "return;"); + ye = "function" === typeof ze.oninput; + } + xe = ye; + } else xe = !1; + we = xe && (!document.documentMode || 9 < document.documentMode); + } + function Ae() { + pe && (pe.detachEvent("onpropertychange", Be), (qe = pe = null)); + } + function Be(a) { + if ("value" === a.propertyName && te(qe)) { + var b = []; + ne(b, qe, a, xb(a)); + a = re; + if (Kb) a(b); + else { + Kb = !0; + try { + Gb(a, b); + } finally { + (Kb = !1), Mb(); + } + } + } + } + function Ce(a, b, c) { + "focusin" === a + ? (Ae(), (pe = b), (qe = c), pe.attachEvent("onpropertychange", Be)) + : "focusout" === a && Ae(); + } + function De(a) { + if ("selectionchange" === a || "keyup" === a || "keydown" === a) + return te(qe); + } + function Ee(a, b) { + if ("click" === a) return te(b); + } + function Fe(a, b) { + if ("input" === a || "change" === a) return te(b); + } + function Ge(a, b) { + return ( + (a === b && (0 !== a || 1 / a === 1 / b)) || (a !== a && b !== b) + ); + } + var He = "function" === typeof Object.is ? Object.is : Ge, + Ie = Object.prototype.hasOwnProperty; + function Je(a, b) { + if (He(a, b)) return !0; + if ( + "object" !== typeof a || + null === a || + "object" !== typeof b || + null === b + ) + return !1; + var c = Object.keys(a), + d = Object.keys(b); + if (c.length !== d.length) return !1; + for (d = 0; d < c.length; d++) + if (!Ie.call(b, c[d]) || !He(a[c[d]], b[c[d]])) return !1; + return !0; + } + function Ke(a) { + for (; a && a.firstChild; ) a = a.firstChild; + return a; + } + function Le(a, b) { + var c = Ke(a); + a = 0; + for (var d; c; ) { + if (3 === c.nodeType) { + d = a + c.textContent.length; + if (a <= b && d >= b) return { node: c, offset: b - a }; + a = d; + } + a: { + for (; c; ) { + if (c.nextSibling) { + c = c.nextSibling; + break a; + } + c = c.parentNode; + } + c = void 0; + } + c = Ke(c); + } + } + function Me(a, b) { + return a && b + ? a === b + ? !0 + : a && 3 === a.nodeType + ? !1 + : b && 3 === b.nodeType + ? Me(a, b.parentNode) + : "contains" in a + ? a.contains(b) + : a.compareDocumentPosition + ? !!(a.compareDocumentPosition(b) & 16) + : !1 + : !1; + } + function Ne() { + for (var a = window, b = Xa(); b instanceof a.HTMLIFrameElement; ) { + try { + var c = "string" === typeof b.contentWindow.location.href; + } catch (d) { + c = !1; + } + if (c) a = b.contentWindow; + else break; + b = Xa(a.document); + } + return b; + } + function Oe(a) { + var b = a && a.nodeName && a.nodeName.toLowerCase(); + return ( + b && + (("input" === b && + ("text" === a.type || + "search" === a.type || + "tel" === a.type || + "url" === a.type || + "password" === a.type)) || + "textarea" === b || + "true" === a.contentEditable) + ); + } + var Pe = fa && "documentMode" in document && 11 >= document.documentMode, + Qe = null, + Re = null, + Se = null, + Te = !1; + function Ue(a, b, c) { + var d = + c.window === c + ? c.document + : 9 === c.nodeType + ? c + : c.ownerDocument; + Te || + null == Qe || + Qe !== Xa(d) || + ((d = Qe), + "selectionStart" in d && Oe(d) + ? (d = { start: d.selectionStart, end: d.selectionEnd }) + : ((d = ( + (d.ownerDocument && d.ownerDocument.defaultView) || + window + ).getSelection()), + (d = { + anchorNode: d.anchorNode, + anchorOffset: d.anchorOffset, + focusNode: d.focusNode, + focusOffset: d.focusOffset, + })), + (Se && Je(Se, d)) || + ((Se = d), + (d = oe(Re, "onSelect")), + 0 < d.length && + ((b = new td("onSelect", "select", null, b, c)), + a.push({ event: b, listeners: d }), + (b.target = Qe)))); + } + Pc( + "cancel cancel click click close close contextmenu contextMenu copy copy cut cut auxclick auxClick dblclick doubleClick dragend dragEnd dragstart dragStart drop drop focusin focus focusout blur input input invalid invalid keydown keyDown keypress keyPress keyup keyUp mousedown mouseDown mouseup mouseUp paste paste pause pause play play pointercancel pointerCancel pointerdown pointerDown pointerup pointerUp ratechange rateChange reset reset seeked seeked submit submit touchcancel touchCancel touchend touchEnd touchstart touchStart volumechange volumeChange".split( + " " + ), + 0 + ); + Pc( + "drag drag dragenter dragEnter dragexit dragExit dragleave dragLeave dragover dragOver mousemove mouseMove mouseout mouseOut mouseover mouseOver pointermove pointerMove pointerout pointerOut pointerover pointerOver scroll scroll toggle toggle touchmove touchMove wheel wheel".split( + " " + ), + 1 + ); + Pc(Oc, 2); + for ( + var Ve = + "change selectionchange textInput compositionstart compositionend compositionupdate".split( + " " + ), + We = 0; + We < Ve.length; + We++ + ) + Nc.set(Ve[We], 0); + ea("onMouseEnter", ["mouseout", "mouseover"]); + ea("onMouseLeave", ["mouseout", "mouseover"]); + ea("onPointerEnter", ["pointerout", "pointerover"]); + ea("onPointerLeave", ["pointerout", "pointerover"]); + da( + "onChange", + "change click focusin focusout input keydown keyup selectionchange".split( + " " + ) + ); + da( + "onSelect", + "focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split( + " " + ) + ); + da("onBeforeInput", ["compositionend", "keypress", "textInput", "paste"]); + da( + "onCompositionEnd", + "compositionend focusout keydown keypress keyup mousedown".split(" ") + ); + da( + "onCompositionStart", + "compositionstart focusout keydown keypress keyup mousedown".split(" ") + ); + da( + "onCompositionUpdate", + "compositionupdate focusout keydown keypress keyup mousedown".split(" ") + ); + var Xe = + "abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split( + " " + ), + Ye = new Set( + "cancel close invalid load scroll toggle".split(" ").concat(Xe) + ); + function Ze(a, b, c) { + var d = a.type || "unknown-event"; + a.currentTarget = c; + Yb(d, b, void 0, a); + a.currentTarget = null; + } + function se(a, b) { + b = 0 !== (b & 4); + for (var c = 0; c < a.length; c++) { + var d = a[c], + e = d.event; + d = d.listeners; + a: { + var f = void 0; + if (b) + for (var g = d.length - 1; 0 <= g; g--) { + var h = d[g], + k = h.instance, + l = h.currentTarget; + h = h.listener; + if (k !== f && e.isPropagationStopped()) break a; + Ze(e, h, l); + f = k; + } + else + for (g = 0; g < d.length; g++) { + h = d[g]; + k = h.instance; + l = h.currentTarget; + h = h.listener; + if (k !== f && e.isPropagationStopped()) break a; + Ze(e, h, l); + f = k; + } + } + } + if (Ub) throw ((a = Vb), (Ub = !1), (Vb = null), a); + } + function G(a, b) { + var c = $e(b), + d = a + "__bubble"; + c.has(d) || (af(b, a, 2, !1), c.add(d)); + } + var bf = "_reactListening" + Math.random().toString(36).slice(2); + function cf(a) { + a[bf] || + ((a[bf] = !0), + ba.forEach(function (b) { + Ye.has(b) || df(b, !1, a, null); + df(b, !0, a, null); + })); + } + function df(a, b, c, d) { + var e = + 4 < arguments.length && void 0 !== arguments[4] + ? arguments[4] + : 0, + f = c; + "selectionchange" === a && 9 !== c.nodeType && (f = c.ownerDocument); + if (null !== d && !b && Ye.has(a)) { + if ("scroll" !== a) return; + e |= 2; + f = d; + } + var g = $e(f), + h = a + "__" + (b ? "capture" : "bubble"); + g.has(h) || (b && (e |= 4), af(f, a, e, b), g.add(h)); + } + function af(a, b, c, d) { + var e = Nc.get(b); + switch (void 0 === e ? 2 : e) { + case 0: + e = gd; + break; + case 1: + e = id; + break; + default: + e = hd; + } + c = e.bind(null, b, c, a); + e = void 0; + !Pb || + ("touchstart" !== b && "touchmove" !== b && "wheel" !== b) || + (e = !0); + d + ? void 0 !== e + ? a.addEventListener(b, c, { capture: !0, passive: e }) + : a.addEventListener(b, c, !0) + : void 0 !== e + ? a.addEventListener(b, c, { passive: e }) + : a.addEventListener(b, c, !1); + } + function jd(a, b, c, d, e) { + var f = d; + if (0 === (b & 1) && 0 === (b & 2) && null !== d) + a: for (;;) { + if (null === d) return; + var g = d.tag; + if (3 === g || 4 === g) { + var h = d.stateNode.containerInfo; + if (h === e || (8 === h.nodeType && h.parentNode === e)) + break; + if (4 === g) + for (g = d.return; null !== g; ) { + var k = g.tag; + if (3 === k || 4 === k) + if ( + ((k = g.stateNode.containerInfo), + k === e || + (8 === k.nodeType && + k.parentNode === e)) + ) + return; + g = g.return; + } + for (; null !== h; ) { + g = wc(h); + if (null === g) return; + k = g.tag; + if (5 === k || 6 === k) { + d = f = g; + continue a; + } + h = h.parentNode; + } + } + d = d.return; + } + Nb(function () { + var d = f, + e = xb(c), + g = []; + a: { + var h = Mc.get(a); + if (void 0 !== h) { + var k = td, + x = a; + switch (a) { + case "keypress": + if (0 === od(c)) break a; + case "keydown": + case "keyup": + k = Rd; + break; + case "focusin": + x = "focus"; + k = Fd; + break; + case "focusout": + x = "blur"; + k = Fd; + break; + case "beforeblur": + case "afterblur": + k = Fd; + break; + case "click": + if (2 === c.button) break a; + case "auxclick": + case "dblclick": + case "mousedown": + case "mousemove": + case "mouseup": + case "mouseout": + case "mouseover": + case "contextmenu": + k = Bd; + break; + case "drag": + case "dragend": + case "dragenter": + case "dragexit": + case "dragleave": + case "dragover": + case "dragstart": + case "drop": + k = Dd; + break; + case "touchcancel": + case "touchend": + case "touchmove": + case "touchstart": + k = Vd; + break; + case Ic: + case Jc: + case Kc: + k = Hd; + break; + case Lc: + k = Xd; + break; + case "scroll": + k = vd; + break; + case "wheel": + k = Zd; + break; + case "copy": + case "cut": + case "paste": + k = Jd; + break; + case "gotpointercapture": + case "lostpointercapture": + case "pointercancel": + case "pointerdown": + case "pointermove": + case "pointerout": + case "pointerover": + case "pointerup": + k = Td; + } + var w = 0 !== (b & 4), + z = !w && "scroll" === a, + u = w ? (null !== h ? h + "Capture" : null) : h; + w = []; + for (var t = d, q; null !== t; ) { + q = t; + var v = q.stateNode; + 5 === q.tag && + null !== v && + ((q = v), + null !== u && + ((v = Ob(t, u)), + null != v && w.push(ef(t, v, q)))); + if (z) break; + t = t.return; + } + 0 < w.length && + ((h = new k(h, x, null, c, e)), + g.push({ event: h, listeners: w })); + } + } + if (0 === (b & 7)) { + a: { + h = "mouseover" === a || "pointerover" === a; + k = "mouseout" === a || "pointerout" === a; + if ( + h && + 0 === (b & 16) && + (x = c.relatedTarget || c.fromElement) && + (wc(x) || x[ff]) + ) + break a; + if (k || h) { + h = + e.window === e + ? e + : (h = e.ownerDocument) + ? h.defaultView || h.parentWindow + : window; + if (k) { + if ( + ((x = c.relatedTarget || c.toElement), + (k = d), + (x = x ? wc(x) : null), + null !== x && + ((z = Zb(x)), + x !== z || (5 !== x.tag && 6 !== x.tag))) + ) + x = null; + } else (k = null), (x = d); + if (k !== x) { + w = Bd; + v = "onMouseLeave"; + u = "onMouseEnter"; + t = "mouse"; + if ("pointerout" === a || "pointerover" === a) + (w = Td), + (v = "onPointerLeave"), + (u = "onPointerEnter"), + (t = "pointer"); + z = null == k ? h : ue(k); + q = null == x ? h : ue(x); + h = new w(v, t + "leave", k, c, e); + h.target = z; + h.relatedTarget = q; + v = null; + wc(e) === d && + ((w = new w(u, t + "enter", x, c, e)), + (w.target = q), + (w.relatedTarget = z), + (v = w)); + z = v; + if (k && x) + b: { + w = k; + u = x; + t = 0; + for (q = w; q; q = gf(q)) t++; + q = 0; + for (v = u; v; v = gf(v)) q++; + for (; 0 < t - q; ) (w = gf(w)), t--; + for (; 0 < q - t; ) (u = gf(u)), q--; + for (; t--; ) { + if ( + w === u || + (null !== u && w === u.alternate) + ) + break b; + w = gf(w); + u = gf(u); + } + w = null; + } + else w = null; + null !== k && hf(g, h, k, w, !1); + null !== x && null !== z && hf(g, z, x, w, !0); + } + } + } + a: { + h = d ? ue(d) : window; + k = h.nodeName && h.nodeName.toLowerCase(); + if ("select" === k || ("input" === k && "file" === h.type)) + var J = ve; + else if (me(h)) + if (we) J = Fe; + else { + J = De; + var K = Ce; + } + else + (k = h.nodeName) && + "input" === k.toLowerCase() && + ("checkbox" === h.type || "radio" === h.type) && + (J = Ee); + if (J && (J = J(a, d))) { + ne(g, J, c, e); + break a; + } + K && K(a, h, d); + "focusout" === a && + (K = h._wrapperState) && + K.controlled && + "number" === h.type && + bb(h, "number", h.value); + } + K = d ? ue(d) : window; + switch (a) { + case "focusin": + if (me(K) || "true" === K.contentEditable) + (Qe = K), (Re = d), (Se = null); + break; + case "focusout": + Se = Re = Qe = null; + break; + case "mousedown": + Te = !0; + break; + case "contextmenu": + case "mouseup": + case "dragend": + Te = !1; + Ue(g, c, e); + break; + case "selectionchange": + if (Pe) break; + case "keydown": + case "keyup": + Ue(g, c, e); + } + var Q; + if (ae) + b: { + switch (a) { + case "compositionstart": + var L = "onCompositionStart"; + break b; + case "compositionend": + L = "onCompositionEnd"; + break b; + case "compositionupdate": + L = "onCompositionUpdate"; + break b; + } + L = void 0; + } + else + ie + ? ge(a, c) && (L = "onCompositionEnd") + : "keydown" === a && + 229 === c.keyCode && + (L = "onCompositionStart"); + L && + (de && + "ko" !== c.locale && + (ie || "onCompositionStart" !== L + ? "onCompositionEnd" === L && ie && (Q = nd()) + : ((kd = e), + (ld = "value" in kd ? kd.value : kd.textContent), + (ie = !0))), + (K = oe(d, L)), + 0 < K.length && + ((L = new Ld(L, a, null, c, e)), + g.push({ event: L, listeners: K }), + Q + ? (L.data = Q) + : ((Q = he(c)), null !== Q && (L.data = Q)))); + if ((Q = ce ? je(a, c) : ke(a, c))) + (d = oe(d, "onBeforeInput")), + 0 < d.length && + ((e = new Ld( + "onBeforeInput", + "beforeinput", + null, + c, + e + )), + g.push({ event: e, listeners: d }), + (e.data = Q)); + } + se(g, b); + }); + } + function ef(a, b, c) { + return { instance: a, listener: b, currentTarget: c }; + } + function oe(a, b) { + for (var c = b + "Capture", d = []; null !== a; ) { + var e = a, + f = e.stateNode; + 5 === e.tag && + null !== f && + ((e = f), + (f = Ob(a, c)), + null != f && d.unshift(ef(a, f, e)), + (f = Ob(a, b)), + null != f && d.push(ef(a, f, e))); + a = a.return; + } + return d; + } + function gf(a) { + if (null === a) return null; + do a = a.return; + while (a && 5 !== a.tag); + return a ? a : null; + } + function hf(a, b, c, d, e) { + for (var f = b._reactName, g = []; null !== c && c !== d; ) { + var h = c, + k = h.alternate, + l = h.stateNode; + if (null !== k && k === d) break; + 5 === h.tag && + null !== l && + ((h = l), + e + ? ((k = Ob(c, f)), null != k && g.unshift(ef(c, k, h))) + : e || ((k = Ob(c, f)), null != k && g.push(ef(c, k, h)))); + c = c.return; + } + 0 !== g.length && a.push({ event: b, listeners: g }); + } + function jf() {} + var kf = null, + lf = null; + function mf(a, b) { + switch (a) { + case "button": + case "input": + case "select": + case "textarea": + return !!b.autoFocus; + } + return !1; + } + function nf(a, b) { + return ( + "textarea" === a || + "option" === a || + "noscript" === a || + "string" === typeof b.children || + "number" === typeof b.children || + ("object" === typeof b.dangerouslySetInnerHTML && + null !== b.dangerouslySetInnerHTML && + null != b.dangerouslySetInnerHTML.__html) + ); + } + var of = "function" === typeof setTimeout ? setTimeout : void 0, + pf = "function" === typeof clearTimeout ? clearTimeout : void 0; + function qf(a) { + 1 === a.nodeType + ? (a.textContent = "") + : 9 === a.nodeType && + ((a = a.body), null != a && (a.textContent = "")); + } + function rf(a) { + for (; null != a; a = a.nextSibling) { + var b = a.nodeType; + if (1 === b || 3 === b) break; + } + return a; + } + function sf(a) { + a = a.previousSibling; + for (var b = 0; a; ) { + if (8 === a.nodeType) { + var c = a.data; + if ("$" === c || "$!" === c || "$?" === c) { + if (0 === b) return a; + b--; + } else "/$" === c && b++; + } + a = a.previousSibling; + } + return null; + } + var tf = 0; + function uf(a) { + return { $$typeof: Ga, toString: a, valueOf: a }; + } + var vf = Math.random().toString(36).slice(2), + wf = "__reactFiber$" + vf, + xf = "__reactProps$" + vf, + ff = "__reactContainer$" + vf, + yf = "__reactEvents$" + vf; + function wc(a) { + var b = a[wf]; + if (b) return b; + for (var c = a.parentNode; c; ) { + if ((b = c[ff] || c[wf])) { + c = b.alternate; + if (null !== b.child || (null !== c && null !== c.child)) + for (a = sf(a); null !== a; ) { + if ((c = a[wf])) return c; + a = sf(a); + } + return b; + } + a = c; + c = a.parentNode; + } + return null; + } + function Cb(a) { + a = a[wf] || a[ff]; + return !a || (5 !== a.tag && 6 !== a.tag && 13 !== a.tag && 3 !== a.tag) + ? null + : a; + } + function ue(a) { + if (5 === a.tag || 6 === a.tag) return a.stateNode; + throw Error(y(33)); + } + function Db(a) { + return a[xf] || null; + } + function $e(a) { + var b = a[yf]; + void 0 === b && (b = a[yf] = new Set()); + return b; + } + var zf = [], + Af = -1; + function Bf(a) { + return { current: a }; + } + function H(a) { + 0 > Af || ((a.current = zf[Af]), (zf[Af] = null), Af--); + } + function I(a, b) { + Af++; + zf[Af] = a.current; + a.current = b; + } + var Cf = {}, + M = Bf(Cf), + N = Bf(!1), + Df = Cf; + function Ef(a, b) { + var c = a.type.contextTypes; + if (!c) return Cf; + var d = a.stateNode; + if (d && d.__reactInternalMemoizedUnmaskedChildContext === b) + return d.__reactInternalMemoizedMaskedChildContext; + var e = {}, + f; + for (f in c) e[f] = b[f]; + d && + ((a = a.stateNode), + (a.__reactInternalMemoizedUnmaskedChildContext = b), + (a.__reactInternalMemoizedMaskedChildContext = e)); + return e; + } + function Ff(a) { + a = a.childContextTypes; + return null !== a && void 0 !== a; + } + function Gf() { + H(N); + H(M); + } + function Hf(a, b, c) { + if (M.current !== Cf) throw Error(y(168)); + I(M, b); + I(N, c); + } + function If(a, b, c) { + var d = a.stateNode; + a = b.childContextTypes; + if ("function" !== typeof d.getChildContext) return c; + d = d.getChildContext(); + for (var e in d) + if (!(e in a)) throw Error(y(108, Ra(b) || "Unknown", e)); + return m({}, c, d); + } + function Jf(a) { + a = + ((a = a.stateNode) && + a.__reactInternalMemoizedMergedChildContext) || + Cf; + Df = M.current; + I(M, a); + I(N, N.current); + return !0; + } + function Kf(a, b, c) { + var d = a.stateNode; + if (!d) throw Error(y(169)); + c + ? ((a = If(a, b, Df)), + (d.__reactInternalMemoizedMergedChildContext = a), + H(N), + H(M), + I(M, a)) + : H(N); + I(N, c); + } + var Lf = null, + Mf = null, + Nf = r.unstable_runWithPriority, + Of = r.unstable_scheduleCallback, + Pf = r.unstable_cancelCallback, + Qf = r.unstable_shouldYield, + Rf = r.unstable_requestPaint, + Sf = r.unstable_now, + Tf = r.unstable_getCurrentPriorityLevel, + Uf = r.unstable_ImmediatePriority, + Vf = r.unstable_UserBlockingPriority, + Wf = r.unstable_NormalPriority, + Xf = r.unstable_LowPriority, + Yf = r.unstable_IdlePriority, + Zf = {}, + $f = void 0 !== Rf ? Rf : function () {}, + ag = null, + bg = null, + cg = !1, + dg = Sf(), + O = + 1e4 > dg + ? Sf + : function () { + return Sf() - dg; + }; + function eg() { + switch (Tf()) { + case Uf: + return 99; + case Vf: + return 98; + case Wf: + return 97; + case Xf: + return 96; + case Yf: + return 95; + default: + throw Error(y(332)); + } + } + function fg(a) { + switch (a) { + case 99: + return Uf; + case 98: + return Vf; + case 97: + return Wf; + case 96: + return Xf; + case 95: + return Yf; + default: + throw Error(y(332)); + } + } + function gg(a, b) { + a = fg(a); + return Nf(a, b); + } + function hg(a, b, c) { + a = fg(a); + return Of(a, b, c); + } + function ig() { + if (null !== bg) { + var a = bg; + bg = null; + Pf(a); + } + jg(); + } + function jg() { + if (!cg && null !== ag) { + cg = !0; + var a = 0; + try { + var b = ag; + gg(99, function () { + for (; a < b.length; a++) { + var c = b[a]; + do c = c(!0); + while (null !== c); + } + }); + ag = null; + } catch (c) { + throw (null !== ag && (ag = ag.slice(a + 1)), Of(Uf, ig), c); + } finally { + cg = !1; + } + } + } + var kg = ra.ReactCurrentBatchConfig; + function lg(a, b) { + if (a && a.defaultProps) { + b = m({}, b); + a = a.defaultProps; + for (var c in a) void 0 === b[c] && (b[c] = a[c]); + return b; + } + return b; + } + var mg = Bf(null), + ng = null, + og = null, + pg = null; + function qg() { + pg = og = ng = null; + } + function rg(a) { + var b = mg.current; + H(mg); + a.type._context._currentValue = b; + } + function sg(a, b) { + for (; null !== a; ) { + var c = a.alternate; + if ((a.childLanes & b) === b) + if (null === c || (c.childLanes & b) === b) break; + else c.childLanes |= b; + else (a.childLanes |= b), null !== c && (c.childLanes |= b); + a = a.return; + } + } + function tg(a, b) { + ng = a; + pg = og = null; + a = a.dependencies; + null !== a && + null !== a.firstContext && + (0 !== (a.lanes & b) && (ug = !0), (a.firstContext = null)); + } + function vg(a, b) { + if (pg !== a && !1 !== b && 0 !== b) { + if ("number" !== typeof b || 1073741823 === b) + (pg = a), (b = 1073741823); + b = { context: a, observedBits: b, next: null }; + if (null === og) { + if (null === ng) throw Error(y(308)); + og = b; + ng.dependencies = { + lanes: 0, + firstContext: b, + responders: null, + }; + } else og = og.next = b; + } + return a._currentValue; + } + var wg = !1; + function xg(a) { + a.updateQueue = { + baseState: a.memoizedState, + firstBaseUpdate: null, + lastBaseUpdate: null, + shared: { pending: null }, + effects: null, + }; + } + function yg(a, b) { + a = a.updateQueue; + b.updateQueue === a && + (b.updateQueue = { + baseState: a.baseState, + firstBaseUpdate: a.firstBaseUpdate, + lastBaseUpdate: a.lastBaseUpdate, + shared: a.shared, + effects: a.effects, + }); + } + function zg(a, b) { + return { + eventTime: a, + lane: b, + tag: 0, + payload: null, + callback: null, + next: null, + }; + } + function Ag(a, b) { + a = a.updateQueue; + if (null !== a) { + a = a.shared; + var c = a.pending; + null === c ? (b.next = b) : ((b.next = c.next), (c.next = b)); + a.pending = b; + } + } + function Bg(a, b) { + var c = a.updateQueue, + d = a.alternate; + if (null !== d && ((d = d.updateQueue), c === d)) { + var e = null, + f = null; + c = c.firstBaseUpdate; + if (null !== c) { + do { + var g = { + eventTime: c.eventTime, + lane: c.lane, + tag: c.tag, + payload: c.payload, + callback: c.callback, + next: null, + }; + null === f ? (e = f = g) : (f = f.next = g); + c = c.next; + } while (null !== c); + null === f ? (e = f = b) : (f = f.next = b); + } else e = f = b; + c = { + baseState: d.baseState, + firstBaseUpdate: e, + lastBaseUpdate: f, + shared: d.shared, + effects: d.effects, + }; + a.updateQueue = c; + return; + } + a = c.lastBaseUpdate; + null === a ? (c.firstBaseUpdate = b) : (a.next = b); + c.lastBaseUpdate = b; + } + function Cg(a, b, c, d) { + var e = a.updateQueue; + wg = !1; + var f = e.firstBaseUpdate, + g = e.lastBaseUpdate, + h = e.shared.pending; + if (null !== h) { + e.shared.pending = null; + var k = h, + l = k.next; + k.next = null; + null === g ? (f = l) : (g.next = l); + g = k; + var n = a.alternate; + if (null !== n) { + n = n.updateQueue; + var A = n.lastBaseUpdate; + A !== g && + (null === A ? (n.firstBaseUpdate = l) : (A.next = l), + (n.lastBaseUpdate = k)); + } + } + if (null !== f) { + A = e.baseState; + g = 0; + n = l = k = null; + do { + h = f.lane; + var p = f.eventTime; + if ((d & h) === h) { + null !== n && + (n = n.next = + { + eventTime: p, + lane: 0, + tag: f.tag, + payload: f.payload, + callback: f.callback, + next: null, + }); + a: { + var C = a, + x = f; + h = b; + p = c; + switch (x.tag) { + case 1: + C = x.payload; + if ("function" === typeof C) { + A = C.call(p, A, h); + break a; + } + A = C; + break a; + case 3: + C.flags = (C.flags & -4097) | 64; + case 0: + C = x.payload; + h = + "function" === typeof C + ? C.call(p, A, h) + : C; + if (null === h || void 0 === h) break a; + A = m({}, A, h); + break a; + case 2: + wg = !0; + } + } + null !== f.callback && + ((a.flags |= 32), + (h = e.effects), + null === h ? (e.effects = [f]) : h.push(f)); + } else + (p = { + eventTime: p, + lane: h, + tag: f.tag, + payload: f.payload, + callback: f.callback, + next: null, + }), + null === n ? ((l = n = p), (k = A)) : (n = n.next = p), + (g |= h); + f = f.next; + if (null === f) + if (((h = e.shared.pending), null === h)) break; + else + (f = h.next), + (h.next = null), + (e.lastBaseUpdate = h), + (e.shared.pending = null); + } while (1); + null === n && (k = A); + e.baseState = k; + e.firstBaseUpdate = l; + e.lastBaseUpdate = n; + Dg |= g; + a.lanes = g; + a.memoizedState = A; + } + } + function Eg(a, b, c) { + a = b.effects; + b.effects = null; + if (null !== a) + for (b = 0; b < a.length; b++) { + var d = a[b], + e = d.callback; + if (null !== e) { + d.callback = null; + d = c; + if ("function" !== typeof e) throw Error(y(191, e)); + e.call(d); + } + } + } + var Fg = new aa.Component().refs; + function Gg(a, b, c, d) { + b = a.memoizedState; + c = c(d, b); + c = null === c || void 0 === c ? b : m({}, b, c); + a.memoizedState = c; + 0 === a.lanes && (a.updateQueue.baseState = c); + } + var Kg = { + isMounted: function (a) { + return (a = a._reactInternals) ? Zb(a) === a : !1; + }, + enqueueSetState: function (a, b, c) { + a = a._reactInternals; + var d = Hg(), + e = Ig(a), + f = zg(d, e); + f.payload = b; + void 0 !== c && null !== c && (f.callback = c); + Ag(a, f); + Jg(a, e, d); + }, + enqueueReplaceState: function (a, b, c) { + a = a._reactInternals; + var d = Hg(), + e = Ig(a), + f = zg(d, e); + f.tag = 1; + f.payload = b; + void 0 !== c && null !== c && (f.callback = c); + Ag(a, f); + Jg(a, e, d); + }, + enqueueForceUpdate: function (a, b) { + a = a._reactInternals; + var c = Hg(), + d = Ig(a), + e = zg(c, d); + e.tag = 2; + void 0 !== b && null !== b && (e.callback = b); + Ag(a, e); + Jg(a, d, c); + }, + }; + function Lg(a, b, c, d, e, f, g) { + a = a.stateNode; + return "function" === typeof a.shouldComponentUpdate + ? a.shouldComponentUpdate(d, f, g) + : b.prototype && b.prototype.isPureReactComponent + ? !Je(c, d) || !Je(e, f) + : !0; + } + function Mg(a, b, c) { + var d = !1, + e = Cf; + var f = b.contextType; + "object" === typeof f && null !== f + ? (f = vg(f)) + : ((e = Ff(b) ? Df : M.current), + (d = b.contextTypes), + (f = (d = null !== d && void 0 !== d) ? Ef(a, e) : Cf)); + b = new b(c, f); + a.memoizedState = + null !== b.state && void 0 !== b.state ? b.state : null; + b.updater = Kg; + a.stateNode = b; + b._reactInternals = a; + d && + ((a = a.stateNode), + (a.__reactInternalMemoizedUnmaskedChildContext = e), + (a.__reactInternalMemoizedMaskedChildContext = f)); + return b; + } + function Ng(a, b, c, d) { + a = b.state; + "function" === typeof b.componentWillReceiveProps && + b.componentWillReceiveProps(c, d); + "function" === typeof b.UNSAFE_componentWillReceiveProps && + b.UNSAFE_componentWillReceiveProps(c, d); + b.state !== a && Kg.enqueueReplaceState(b, b.state, null); + } + function Og(a, b, c, d) { + var e = a.stateNode; + e.props = c; + e.state = a.memoizedState; + e.refs = Fg; + xg(a); + var f = b.contextType; + "object" === typeof f && null !== f + ? (e.context = vg(f)) + : ((f = Ff(b) ? Df : M.current), (e.context = Ef(a, f))); + Cg(a, c, e, d); + e.state = a.memoizedState; + f = b.getDerivedStateFromProps; + "function" === typeof f && + (Gg(a, b, f, c), (e.state = a.memoizedState)); + "function" === typeof b.getDerivedStateFromProps || + "function" === typeof e.getSnapshotBeforeUpdate || + ("function" !== typeof e.UNSAFE_componentWillMount && + "function" !== typeof e.componentWillMount) || + ((b = e.state), + "function" === typeof e.componentWillMount && + e.componentWillMount(), + "function" === typeof e.UNSAFE_componentWillMount && + e.UNSAFE_componentWillMount(), + b !== e.state && Kg.enqueueReplaceState(e, e.state, null), + Cg(a, c, e, d), + (e.state = a.memoizedState)); + "function" === typeof e.componentDidMount && (a.flags |= 4); + } + var Pg = Array.isArray; + function Qg(a, b, c) { + a = c.ref; + if (null !== a && "function" !== typeof a && "object" !== typeof a) { + if (c._owner) { + c = c._owner; + if (c) { + if (1 !== c.tag) throw Error(y(309)); + var d = c.stateNode; + } + if (!d) throw Error(y(147, a)); + var e = "" + a; + if ( + null !== b && + null !== b.ref && + "function" === typeof b.ref && + b.ref._stringRef === e + ) + return b.ref; + b = function (a) { + var b = d.refs; + b === Fg && (b = d.refs = {}); + null === a ? delete b[e] : (b[e] = a); + }; + b._stringRef = e; + return b; + } + if ("string" !== typeof a) throw Error(y(284)); + if (!c._owner) throw Error(y(290, a)); + } + return a; + } + function Rg(a, b) { + if ("textarea" !== a.type) + throw Error( + y( + 31, + "[object Object]" === Object.prototype.toString.call(b) + ? "object with keys {" + Object.keys(b).join(", ") + "}" + : b + ) + ); + } + function Sg(a) { + function b(b, c) { + if (a) { + var d = b.lastEffect; + null !== d + ? ((d.nextEffect = c), (b.lastEffect = c)) + : (b.firstEffect = b.lastEffect = c); + c.nextEffect = null; + c.flags = 8; + } + } + function c(c, d) { + if (!a) return null; + for (; null !== d; ) b(c, d), (d = d.sibling); + return null; + } + function d(a, b) { + for (a = new Map(); null !== b; ) + null !== b.key ? a.set(b.key, b) : a.set(b.index, b), + (b = b.sibling); + return a; + } + function e(a, b) { + a = Tg(a, b); + a.index = 0; + a.sibling = null; + return a; + } + function f(b, c, d) { + b.index = d; + if (!a) return c; + d = b.alternate; + if (null !== d) + return (d = d.index), d < c ? ((b.flags = 2), c) : d; + b.flags = 2; + return c; + } + function g(b) { + a && null === b.alternate && (b.flags = 2); + return b; + } + function h(a, b, c, d) { + if (null === b || 6 !== b.tag) + return (b = Ug(c, a.mode, d)), (b.return = a), b; + b = e(b, c); + b.return = a; + return b; + } + function k(a, b, c, d) { + if (null !== b && b.elementType === c.type) + return ( + (d = e(b, c.props)), + (d.ref = Qg(a, b, c)), + (d.return = a), + d + ); + d = Vg(c.type, c.key, c.props, null, a.mode, d); + d.ref = Qg(a, b, c); + d.return = a; + return d; + } + function l(a, b, c, d) { + if ( + null === b || + 4 !== b.tag || + b.stateNode.containerInfo !== c.containerInfo || + b.stateNode.implementation !== c.implementation + ) + return (b = Wg(c, a.mode, d)), (b.return = a), b; + b = e(b, c.children || []); + b.return = a; + return b; + } + function n(a, b, c, d, f) { + if (null === b || 7 !== b.tag) + return (b = Xg(c, a.mode, d, f)), (b.return = a), b; + b = e(b, c); + b.return = a; + return b; + } + function A(a, b, c) { + if ("string" === typeof b || "number" === typeof b) + return (b = Ug("" + b, a.mode, c)), (b.return = a), b; + if ("object" === typeof b && null !== b) { + switch (b.$$typeof) { + case sa: + return ( + (c = Vg(b.type, b.key, b.props, null, a.mode, c)), + (c.ref = Qg(a, null, b)), + (c.return = a), + c + ); + case ta: + return (b = Wg(b, a.mode, c)), (b.return = a), b; + } + if (Pg(b) || La(b)) + return (b = Xg(b, a.mode, c, null)), (b.return = a), b; + Rg(a, b); + } + return null; + } + function p(a, b, c, d) { + var e = null !== b ? b.key : null; + if ("string" === typeof c || "number" === typeof c) + return null !== e ? null : h(a, b, "" + c, d); + if ("object" === typeof c && null !== c) { + switch (c.$$typeof) { + case sa: + return c.key === e + ? c.type === ua + ? n(a, b, c.props.children, d, e) + : k(a, b, c, d) + : null; + case ta: + return c.key === e ? l(a, b, c, d) : null; + } + if (Pg(c) || La(c)) + return null !== e ? null : n(a, b, c, d, null); + Rg(a, c); + } + return null; + } + function C(a, b, c, d, e) { + if ("string" === typeof d || "number" === typeof d) + return (a = a.get(c) || null), h(b, a, "" + d, e); + if ("object" === typeof d && null !== d) { + switch (d.$$typeof) { + case sa: + return ( + (a = a.get(null === d.key ? c : d.key) || null), + d.type === ua + ? n(b, a, d.props.children, e, d.key) + : k(b, a, d, e) + ); + case ta: + return ( + (a = a.get(null === d.key ? c : d.key) || null), + l(b, a, d, e) + ); + } + if (Pg(d) || La(d)) + return (a = a.get(c) || null), n(b, a, d, e, null); + Rg(b, d); + } + return null; + } + function x(e, g, h, k) { + for ( + var l = null, t = null, u = g, z = (g = 0), q = null; + null !== u && z < h.length; + z++ + ) { + u.index > z ? ((q = u), (u = null)) : (q = u.sibling); + var n = p(e, u, h[z], k); + if (null === n) { + null === u && (u = q); + break; + } + a && u && null === n.alternate && b(e, u); + g = f(n, g, z); + null === t ? (l = n) : (t.sibling = n); + t = n; + u = q; + } + if (z === h.length) return c(e, u), l; + if (null === u) { + for (; z < h.length; z++) + (u = A(e, h[z], k)), + null !== u && + ((g = f(u, g, z)), + null === t ? (l = u) : (t.sibling = u), + (t = u)); + return l; + } + for (u = d(e, u); z < h.length; z++) + (q = C(u, e, z, h[z], k)), + null !== q && + (a && + null !== q.alternate && + u.delete(null === q.key ? z : q.key), + (g = f(q, g, z)), + null === t ? (l = q) : (t.sibling = q), + (t = q)); + a && + u.forEach(function (a) { + return b(e, a); + }); + return l; + } + function w(e, g, h, k) { + var l = La(h); + if ("function" !== typeof l) throw Error(y(150)); + h = l.call(h); + if (null == h) throw Error(y(151)); + for ( + var t = (l = null), u = g, z = (g = 0), q = null, n = h.next(); + null !== u && !n.done; + z++, n = h.next() + ) { + u.index > z ? ((q = u), (u = null)) : (q = u.sibling); + var w = p(e, u, n.value, k); + if (null === w) { + null === u && (u = q); + break; + } + a && u && null === w.alternate && b(e, u); + g = f(w, g, z); + null === t ? (l = w) : (t.sibling = w); + t = w; + u = q; + } + if (n.done) return c(e, u), l; + if (null === u) { + for (; !n.done; z++, n = h.next()) + (n = A(e, n.value, k)), + null !== n && + ((g = f(n, g, z)), + null === t ? (l = n) : (t.sibling = n), + (t = n)); + return l; + } + for (u = d(e, u); !n.done; z++, n = h.next()) + (n = C(u, e, z, n.value, k)), + null !== n && + (a && + null !== n.alternate && + u.delete(null === n.key ? z : n.key), + (g = f(n, g, z)), + null === t ? (l = n) : (t.sibling = n), + (t = n)); + a && + u.forEach(function (a) { + return b(e, a); + }); + return l; + } + return function (a, d, f, h) { + var k = + "object" === typeof f && + null !== f && + f.type === ua && + null === f.key; + k && (f = f.props.children); + var l = "object" === typeof f && null !== f; + if (l) + switch (f.$$typeof) { + case sa: + a: { + l = f.key; + for (k = d; null !== k; ) { + if (k.key === l) { + switch (k.tag) { + case 7: + if (f.type === ua) { + c(a, k.sibling); + d = e(k, f.props.children); + d.return = a; + a = d; + break a; + } + break; + default: + if (k.elementType === f.type) { + c(a, k.sibling); + d = e(k, f.props); + d.ref = Qg(a, k, f); + d.return = a; + a = d; + break a; + } + } + c(a, k); + break; + } else b(a, k); + k = k.sibling; + } + f.type === ua + ? ((d = Xg(f.props.children, a.mode, h, f.key)), + (d.return = a), + (a = d)) + : ((h = Vg( + f.type, + f.key, + f.props, + null, + a.mode, + h + )), + (h.ref = Qg(a, d, f)), + (h.return = a), + (a = h)); + } + return g(a); + case ta: + a: { + for (k = f.key; null !== d; ) { + if (d.key === k) + if ( + 4 === d.tag && + d.stateNode.containerInfo === + f.containerInfo && + d.stateNode.implementation === + f.implementation + ) { + c(a, d.sibling); + d = e(d, f.children || []); + d.return = a; + a = d; + break a; + } else { + c(a, d); + break; + } + else b(a, d); + d = d.sibling; + } + d = Wg(f, a.mode, h); + d.return = a; + a = d; + } + return g(a); + } + if ("string" === typeof f || "number" === typeof f) + return ( + (f = "" + f), + null !== d && 6 === d.tag + ? (c(a, d.sibling), + (d = e(d, f)), + (d.return = a), + (a = d)) + : (c(a, d), + (d = Ug(f, a.mode, h)), + (d.return = a), + (a = d)), + g(a) + ); + if (Pg(f)) return x(a, d, f, h); + if (La(f)) return w(a, d, f, h); + l && Rg(a, f); + if ("undefined" === typeof f && !k) + switch (a.tag) { + case 1: + case 22: + case 0: + case 11: + case 15: + throw Error(y(152, Ra(a.type) || "Component")); + } + return c(a, d); + }; + } + var Yg = Sg(!0), + Zg = Sg(!1), + $g = {}, + ah = Bf($g), + bh = Bf($g), + ch = Bf($g); + function dh(a) { + if (a === $g) throw Error(y(174)); + return a; + } + function eh(a, b) { + I(ch, b); + I(bh, a); + I(ah, $g); + a = b.nodeType; + switch (a) { + case 9: + case 11: + b = (b = b.documentElement) ? b.namespaceURI : mb(null, ""); + break; + default: + (a = 8 === a ? b.parentNode : b), + (b = a.namespaceURI || null), + (a = a.tagName), + (b = mb(b, a)); + } + H(ah); + I(ah, b); + } + function fh() { + H(ah); + H(bh); + H(ch); + } + function gh(a) { + dh(ch.current); + var b = dh(ah.current); + var c = mb(b, a.type); + b !== c && (I(bh, a), I(ah, c)); + } + function hh(a) { + bh.current === a && (H(ah), H(bh)); + } + var P = Bf(0); + function ih(a) { + for (var b = a; null !== b; ) { + if (13 === b.tag) { + var c = b.memoizedState; + if ( + null !== c && + ((c = c.dehydrated), + null === c || "$?" === c.data || "$!" === c.data) + ) + return b; + } else if (19 === b.tag && void 0 !== b.memoizedProps.revealOrder) { + if (0 !== (b.flags & 64)) return b; + } else if (null !== b.child) { + b.child.return = b; + b = b.child; + continue; + } + if (b === a) break; + for (; null === b.sibling; ) { + if (null === b.return || b.return === a) return null; + b = b.return; + } + b.sibling.return = b.return; + b = b.sibling; + } + return null; + } + var jh = null, + kh = null, + lh = !1; + function mh(a, b) { + var c = nh(5, null, null, 0); + c.elementType = "DELETED"; + c.type = "DELETED"; + c.stateNode = b; + c.return = a; + c.flags = 8; + null !== a.lastEffect + ? ((a.lastEffect.nextEffect = c), (a.lastEffect = c)) + : (a.firstEffect = a.lastEffect = c); + } + function oh(a, b) { + switch (a.tag) { + case 5: + var c = a.type; + b = + 1 !== b.nodeType || + c.toLowerCase() !== b.nodeName.toLowerCase() + ? null + : b; + return null !== b ? ((a.stateNode = b), !0) : !1; + case 6: + return ( + (b = "" === a.pendingProps || 3 !== b.nodeType ? null : b), + null !== b ? ((a.stateNode = b), !0) : !1 + ); + case 13: + return !1; + default: + return !1; + } + } + function ph(a) { + if (lh) { + var b = kh; + if (b) { + var c = b; + if (!oh(a, b)) { + b = rf(c.nextSibling); + if (!b || !oh(a, b)) { + a.flags = (a.flags & -1025) | 2; + lh = !1; + jh = a; + return; + } + mh(jh, c); + } + jh = a; + kh = rf(b.firstChild); + } else (a.flags = (a.flags & -1025) | 2), (lh = !1), (jh = a); + } + } + function qh(a) { + for ( + a = a.return; + null !== a && 5 !== a.tag && 3 !== a.tag && 13 !== a.tag; + + ) + a = a.return; + jh = a; + } + function rh(a) { + if (a !== jh) return !1; + if (!lh) return qh(a), (lh = !0), !1; + var b = a.type; + if ( + 5 !== a.tag || + ("head" !== b && "body" !== b && !nf(b, a.memoizedProps)) + ) + for (b = kh; b; ) mh(a, b), (b = rf(b.nextSibling)); + qh(a); + if (13 === a.tag) { + a = a.memoizedState; + a = null !== a ? a.dehydrated : null; + if (!a) throw Error(y(317)); + a: { + a = a.nextSibling; + for (b = 0; a; ) { + if (8 === a.nodeType) { + var c = a.data; + if ("/$" === c) { + if (0 === b) { + kh = rf(a.nextSibling); + break a; + } + b--; + } else ("$" !== c && "$!" !== c && "$?" !== c) || b++; + } + a = a.nextSibling; + } + kh = null; + } + } else kh = jh ? rf(a.stateNode.nextSibling) : null; + return !0; + } + function sh() { + kh = jh = null; + lh = !1; + } + var th = []; + function uh() { + for (var a = 0; a < th.length; a++) + th[a]._workInProgressVersionPrimary = null; + th.length = 0; + } + var vh = ra.ReactCurrentDispatcher, + wh = ra.ReactCurrentBatchConfig, + xh = 0, + R = null, + S = null, + T = null, + yh = !1, + zh = !1; + function Ah() { + throw Error(y(321)); + } + function Bh(a, b) { + if (null === b) return !1; + for (var c = 0; c < b.length && c < a.length; c++) + if (!He(a[c], b[c])) return !1; + return !0; + } + function Ch(a, b, c, d, e, f) { + xh = f; + R = b; + b.memoizedState = null; + b.updateQueue = null; + b.lanes = 0; + vh.current = null === a || null === a.memoizedState ? Dh : Eh; + a = c(d, e); + if (zh) { + f = 0; + do { + zh = !1; + if (!(25 > f)) throw Error(y(301)); + f += 1; + T = S = null; + b.updateQueue = null; + vh.current = Fh; + a = c(d, e); + } while (zh); + } + vh.current = Gh; + b = null !== S && null !== S.next; + xh = 0; + T = S = R = null; + yh = !1; + if (b) throw Error(y(300)); + return a; + } + function Hh() { + var a = { + memoizedState: null, + baseState: null, + baseQueue: null, + queue: null, + next: null, + }; + null === T ? (R.memoizedState = T = a) : (T = T.next = a); + return T; + } + function Ih() { + if (null === S) { + var a = R.alternate; + a = null !== a ? a.memoizedState : null; + } else a = S.next; + var b = null === T ? R.memoizedState : T.next; + if (null !== b) (T = b), (S = a); + else { + if (null === a) throw Error(y(310)); + S = a; + a = { + memoizedState: S.memoizedState, + baseState: S.baseState, + baseQueue: S.baseQueue, + queue: S.queue, + next: null, + }; + null === T ? (R.memoizedState = T = a) : (T = T.next = a); + } + return T; + } + function Jh(a, b) { + return "function" === typeof b ? b(a) : b; + } + function Kh(a) { + var b = Ih(), + c = b.queue; + if (null === c) throw Error(y(311)); + c.lastRenderedReducer = a; + var d = S, + e = d.baseQueue, + f = c.pending; + if (null !== f) { + if (null !== e) { + var g = e.next; + e.next = f.next; + f.next = g; + } + d.baseQueue = e = f; + c.pending = null; + } + if (null !== e) { + e = e.next; + d = d.baseState; + var h = (g = f = null), + k = e; + do { + var l = k.lane; + if ((xh & l) === l) + null !== h && + (h = h.next = + { + lane: 0, + action: k.action, + eagerReducer: k.eagerReducer, + eagerState: k.eagerState, + next: null, + }), + (d = + k.eagerReducer === a + ? k.eagerState + : a(d, k.action)); + else { + var n = { + lane: l, + action: k.action, + eagerReducer: k.eagerReducer, + eagerState: k.eagerState, + next: null, + }; + null === h ? ((g = h = n), (f = d)) : (h = h.next = n); + R.lanes |= l; + Dg |= l; + } + k = k.next; + } while (null !== k && k !== e); + null === h ? (f = d) : (h.next = g); + He(d, b.memoizedState) || (ug = !0); + b.memoizedState = d; + b.baseState = f; + b.baseQueue = h; + c.lastRenderedState = d; + } + return [b.memoizedState, c.dispatch]; + } + function Lh(a) { + var b = Ih(), + c = b.queue; + if (null === c) throw Error(y(311)); + c.lastRenderedReducer = a; + var d = c.dispatch, + e = c.pending, + f = b.memoizedState; + if (null !== e) { + c.pending = null; + var g = (e = e.next); + do (f = a(f, g.action)), (g = g.next); + while (g !== e); + He(f, b.memoizedState) || (ug = !0); + b.memoizedState = f; + null === b.baseQueue && (b.baseState = f); + c.lastRenderedState = f; + } + return [f, d]; + } + function Mh(a, b, c) { + var d = b._getVersion; + d = d(b._source); + var e = b._workInProgressVersionPrimary; + if (null !== e) a = e === d; + else if (((a = a.mutableReadLanes), (a = (xh & a) === a))) + (b._workInProgressVersionPrimary = d), th.push(b); + if (a) return c(b._source); + th.push(b); + throw Error(y(350)); + } + function Nh(a, b, c, d) { + var e = U; + if (null === e) throw Error(y(349)); + var f = b._getVersion, + g = f(b._source), + h = vh.current, + k = h.useState(function () { + return Mh(e, b, c); + }), + l = k[1], + n = k[0]; + k = T; + var A = a.memoizedState, + p = A.refs, + C = p.getSnapshot, + x = A.source; + A = A.subscribe; + var w = R; + a.memoizedState = { refs: p, source: b, subscribe: d }; + h.useEffect( + function () { + p.getSnapshot = c; + p.setSnapshot = l; + var a = f(b._source); + if (!He(g, a)) { + a = c(b._source); + He(n, a) || + (l(a), + (a = Ig(w)), + (e.mutableReadLanes |= a & e.pendingLanes)); + a = e.mutableReadLanes; + e.entangledLanes |= a; + for (var d = e.entanglements, h = a; 0 < h; ) { + var k = 31 - Vc(h), + v = 1 << k; + d[k] |= a; + h &= ~v; + } + } + }, + [c, b, d] + ); + h.useEffect( + function () { + return d(b._source, function () { + var a = p.getSnapshot, + c = p.setSnapshot; + try { + c(a(b._source)); + var d = Ig(w); + e.mutableReadLanes |= d & e.pendingLanes; + } catch (q) { + c(function () { + throw q; + }); + } + }); + }, + [b, d] + ); + (He(C, c) && He(x, b) && He(A, d)) || + ((a = { + pending: null, + dispatch: null, + lastRenderedReducer: Jh, + lastRenderedState: n, + }), + (a.dispatch = l = Oh.bind(null, R, a)), + (k.queue = a), + (k.baseQueue = null), + (n = Mh(e, b, c)), + (k.memoizedState = k.baseState = n)); + return n; + } + function Ph(a, b, c) { + var d = Ih(); + return Nh(d, a, b, c); + } + function Qh(a) { + var b = Hh(); + "function" === typeof a && (a = a()); + b.memoizedState = b.baseState = a; + a = b.queue = { + pending: null, + dispatch: null, + lastRenderedReducer: Jh, + lastRenderedState: a, + }; + a = a.dispatch = Oh.bind(null, R, a); + return [b.memoizedState, a]; + } + function Rh(a, b, c, d) { + a = { tag: a, create: b, destroy: c, deps: d, next: null }; + b = R.updateQueue; + null === b + ? ((b = { lastEffect: null }), + (R.updateQueue = b), + (b.lastEffect = a.next = a)) + : ((c = b.lastEffect), + null === c + ? (b.lastEffect = a.next = a) + : ((d = c.next), + (c.next = a), + (a.next = d), + (b.lastEffect = a))); + return a; + } + function Sh(a) { + var b = Hh(); + a = { current: a }; + return (b.memoizedState = a); + } + function Th() { + return Ih().memoizedState; + } + function Uh(a, b, c, d) { + var e = Hh(); + R.flags |= a; + e.memoizedState = Rh(1 | b, c, void 0, void 0 === d ? null : d); + } + function Vh(a, b, c, d) { + var e = Ih(); + d = void 0 === d ? null : d; + var f = void 0; + if (null !== S) { + var g = S.memoizedState; + f = g.destroy; + if (null !== d && Bh(d, g.deps)) { + Rh(b, c, f, d); + return; + } + } + R.flags |= a; + e.memoizedState = Rh(1 | b, c, f, d); + } + function Wh(a, b) { + return Uh(516, 4, a, b); + } + function Xh(a, b) { + return Vh(516, 4, a, b); + } + function Yh(a, b) { + return Vh(4, 2, a, b); + } + function Zh(a, b) { + if ("function" === typeof b) + return ( + (a = a()), + b(a), + function () { + b(null); + } + ); + if (null !== b && void 0 !== b) + return ( + (a = a()), + (b.current = a), + function () { + b.current = null; + } + ); + } + function $h(a, b, c) { + c = null !== c && void 0 !== c ? c.concat([a]) : null; + return Vh(4, 2, Zh.bind(null, b, a), c); + } + function ai() {} + function bi(a, b) { + var c = Ih(); + b = void 0 === b ? null : b; + var d = c.memoizedState; + if (null !== d && null !== b && Bh(b, d[1])) return d[0]; + c.memoizedState = [a, b]; + return a; + } + function ci(a, b) { + var c = Ih(); + b = void 0 === b ? null : b; + var d = c.memoizedState; + if (null !== d && null !== b && Bh(b, d[1])) return d[0]; + a = a(); + c.memoizedState = [a, b]; + return a; + } + function di(a, b) { + var c = eg(); + gg(98 > c ? 98 : c, function () { + a(!0); + }); + gg(97 < c ? 97 : c, function () { + var c = wh.transition; + wh.transition = 1; + try { + a(!1), b(); + } finally { + wh.transition = c; + } + }); + } + function Oh(a, b, c) { + var d = Hg(), + e = Ig(a), + f = { + lane: e, + action: c, + eagerReducer: null, + eagerState: null, + next: null, + }, + g = b.pending; + null === g ? (f.next = f) : ((f.next = g.next), (g.next = f)); + b.pending = f; + g = a.alternate; + if (a === R || (null !== g && g === R)) zh = yh = !0; + else { + if ( + 0 === a.lanes && + (null === g || 0 === g.lanes) && + ((g = b.lastRenderedReducer), null !== g) + ) + try { + var h = b.lastRenderedState, + k = g(h, c); + f.eagerReducer = g; + f.eagerState = k; + if (He(k, h)) return; + } catch (l) { + } finally { + } + Jg(a, e, d); + } + } + var Gh = { + readContext: vg, + useCallback: Ah, + useContext: Ah, + useEffect: Ah, + useImperativeHandle: Ah, + useLayoutEffect: Ah, + useMemo: Ah, + useReducer: Ah, + useRef: Ah, + useState: Ah, + useDebugValue: Ah, + useDeferredValue: Ah, + useTransition: Ah, + useMutableSource: Ah, + useOpaqueIdentifier: Ah, + unstable_isNewReconciler: !1, + }, + Dh = { + readContext: vg, + useCallback: function (a, b) { + Hh().memoizedState = [a, void 0 === b ? null : b]; + return a; + }, + useContext: vg, + useEffect: Wh, + useImperativeHandle: function (a, b, c) { + c = null !== c && void 0 !== c ? c.concat([a]) : null; + return Uh(4, 2, Zh.bind(null, b, a), c); + }, + useLayoutEffect: function (a, b) { + return Uh(4, 2, a, b); + }, + useMemo: function (a, b) { + var c = Hh(); + b = void 0 === b ? null : b; + a = a(); + c.memoizedState = [a, b]; + return a; + }, + useReducer: function (a, b, c) { + var d = Hh(); + b = void 0 !== c ? c(b) : b; + d.memoizedState = d.baseState = b; + a = d.queue = { + pending: null, + dispatch: null, + lastRenderedReducer: a, + lastRenderedState: b, + }; + a = a.dispatch = Oh.bind(null, R, a); + return [d.memoizedState, a]; + }, + useRef: Sh, + useState: Qh, + useDebugValue: ai, + useDeferredValue: function (a) { + var b = Qh(a), + c = b[0], + d = b[1]; + Wh( + function () { + var b = wh.transition; + wh.transition = 1; + try { + d(a); + } finally { + wh.transition = b; + } + }, + [a] + ); + return c; + }, + useTransition: function () { + var a = Qh(!1), + b = a[0]; + a = di.bind(null, a[1]); + Sh(a); + return [a, b]; + }, + useMutableSource: function (a, b, c) { + var d = Hh(); + d.memoizedState = { + refs: { getSnapshot: b, setSnapshot: null }, + source: a, + subscribe: c, + }; + return Nh(d, a, b, c); + }, + useOpaqueIdentifier: function () { + if (lh) { + var a = !1, + b = uf(function () { + a || ((a = !0), c("r:" + (tf++).toString(36))); + throw Error(y(355)); + }), + c = Qh(b)[1]; + 0 === (R.mode & 2) && + ((R.flags |= 516), + Rh( + 5, + function () { + c("r:" + (tf++).toString(36)); + }, + void 0, + null + )); + return b; + } + b = "r:" + (tf++).toString(36); + Qh(b); + return b; + }, + unstable_isNewReconciler: !1, + }, + Eh = { + readContext: vg, + useCallback: bi, + useContext: vg, + useEffect: Xh, + useImperativeHandle: $h, + useLayoutEffect: Yh, + useMemo: ci, + useReducer: Kh, + useRef: Th, + useState: function () { + return Kh(Jh); + }, + useDebugValue: ai, + useDeferredValue: function (a) { + var b = Kh(Jh), + c = b[0], + d = b[1]; + Xh( + function () { + var b = wh.transition; + wh.transition = 1; + try { + d(a); + } finally { + wh.transition = b; + } + }, + [a] + ); + return c; + }, + useTransition: function () { + var a = Kh(Jh)[0]; + return [Th().current, a]; + }, + useMutableSource: Ph, + useOpaqueIdentifier: function () { + return Kh(Jh)[0]; + }, + unstable_isNewReconciler: !1, + }, + Fh = { + readContext: vg, + useCallback: bi, + useContext: vg, + useEffect: Xh, + useImperativeHandle: $h, + useLayoutEffect: Yh, + useMemo: ci, + useReducer: Lh, + useRef: Th, + useState: function () { + return Lh(Jh); + }, + useDebugValue: ai, + useDeferredValue: function (a) { + var b = Lh(Jh), + c = b[0], + d = b[1]; + Xh( + function () { + var b = wh.transition; + wh.transition = 1; + try { + d(a); + } finally { + wh.transition = b; + } + }, + [a] + ); + return c; + }, + useTransition: function () { + var a = Lh(Jh)[0]; + return [Th().current, a]; + }, + useMutableSource: Ph, + useOpaqueIdentifier: function () { + return Lh(Jh)[0]; + }, + unstable_isNewReconciler: !1, + }, + ei = ra.ReactCurrentOwner, + ug = !1; + function fi(a, b, c, d) { + b.child = null === a ? Zg(b, null, c, d) : Yg(b, a.child, c, d); + } + function gi(a, b, c, d, e) { + c = c.render; + var f = b.ref; + tg(b, e); + d = Ch(a, b, c, d, f, e); + if (null !== a && !ug) + return ( + (b.updateQueue = a.updateQueue), + (b.flags &= -517), + (a.lanes &= ~e), + hi(a, b, e) + ); + b.flags |= 1; + fi(a, b, d, e); + return b.child; + } + function ii(a, b, c, d, e, f) { + if (null === a) { + var g = c.type; + if ( + "function" === typeof g && + !ji(g) && + void 0 === g.defaultProps && + null === c.compare && + void 0 === c.defaultProps + ) + return (b.tag = 15), (b.type = g), ki(a, b, g, d, e, f); + a = Vg(c.type, null, d, b, b.mode, f); + a.ref = b.ref; + a.return = b; + return (b.child = a); + } + g = a.child; + if ( + 0 === (e & f) && + ((e = g.memoizedProps), + (c = c.compare), + (c = null !== c ? c : Je), + c(e, d) && a.ref === b.ref) + ) + return hi(a, b, f); + b.flags |= 1; + a = Tg(g, d); + a.ref = b.ref; + a.return = b; + return (b.child = a); + } + function ki(a, b, c, d, e, f) { + if (null !== a && Je(a.memoizedProps, d) && a.ref === b.ref) + if (((ug = !1), 0 !== (f & e))) + 0 !== (a.flags & 16384) && (ug = !0); + else return (b.lanes = a.lanes), hi(a, b, f); + return li(a, b, c, d, f); + } + function mi(a, b, c) { + var d = b.pendingProps, + e = d.children, + f = null !== a ? a.memoizedState : null; + if ("hidden" === d.mode || "unstable-defer-without-hiding" === d.mode) + if (0 === (b.mode & 4)) + (b.memoizedState = { baseLanes: 0 }), ni(b, c); + else if (0 !== (c & 1073741824)) + (b.memoizedState = { baseLanes: 0 }), + ni(b, null !== f ? f.baseLanes : c); + else + return ( + (a = null !== f ? f.baseLanes | c : c), + (b.lanes = b.childLanes = 1073741824), + (b.memoizedState = { baseLanes: a }), + ni(b, a), + null + ); + else + null !== f + ? ((d = f.baseLanes | c), (b.memoizedState = null)) + : (d = c), + ni(b, d); + fi(a, b, e, c); + return b.child; + } + function oi(a, b) { + var c = b.ref; + if ((null === a && null !== c) || (null !== a && a.ref !== c)) + b.flags |= 128; + } + function li(a, b, c, d, e) { + var f = Ff(c) ? Df : M.current; + f = Ef(b, f); + tg(b, e); + c = Ch(a, b, c, d, f, e); + if (null !== a && !ug) + return ( + (b.updateQueue = a.updateQueue), + (b.flags &= -517), + (a.lanes &= ~e), + hi(a, b, e) + ); + b.flags |= 1; + fi(a, b, c, e); + return b.child; + } + function pi(a, b, c, d, e) { + if (Ff(c)) { + var f = !0; + Jf(b); + } else f = !1; + tg(b, e); + if (null === b.stateNode) + null !== a && + ((a.alternate = null), (b.alternate = null), (b.flags |= 2)), + Mg(b, c, d), + Og(b, c, d, e), + (d = !0); + else if (null === a) { + var g = b.stateNode, + h = b.memoizedProps; + g.props = h; + var k = g.context, + l = c.contextType; + "object" === typeof l && null !== l + ? (l = vg(l)) + : ((l = Ff(c) ? Df : M.current), (l = Ef(b, l))); + var n = c.getDerivedStateFromProps, + A = + "function" === typeof n || + "function" === typeof g.getSnapshotBeforeUpdate; + A || + ("function" !== typeof g.UNSAFE_componentWillReceiveProps && + "function" !== typeof g.componentWillReceiveProps) || + ((h !== d || k !== l) && Ng(b, g, d, l)); + wg = !1; + var p = b.memoizedState; + g.state = p; + Cg(b, d, g, e); + k = b.memoizedState; + h !== d || p !== k || N.current || wg + ? ("function" === typeof n && + (Gg(b, c, n, d), (k = b.memoizedState)), + (h = wg || Lg(b, c, h, d, p, k, l)) + ? (A || + ("function" !== + typeof g.UNSAFE_componentWillMount && + "function" !== + typeof g.componentWillMount) || + ("function" === typeof g.componentWillMount && + g.componentWillMount(), + "function" === + typeof g.UNSAFE_componentWillMount && + g.UNSAFE_componentWillMount()), + "function" === typeof g.componentDidMount && + (b.flags |= 4)) + : ("function" === typeof g.componentDidMount && + (b.flags |= 4), + (b.memoizedProps = d), + (b.memoizedState = k)), + (g.props = d), + (g.state = k), + (g.context = l), + (d = h)) + : ("function" === typeof g.componentDidMount && (b.flags |= 4), + (d = !1)); + } else { + g = b.stateNode; + yg(a, b); + h = b.memoizedProps; + l = b.type === b.elementType ? h : lg(b.type, h); + g.props = l; + A = b.pendingProps; + p = g.context; + k = c.contextType; + "object" === typeof k && null !== k + ? (k = vg(k)) + : ((k = Ff(c) ? Df : M.current), (k = Ef(b, k))); + var C = c.getDerivedStateFromProps; + (n = + "function" === typeof C || + "function" === typeof g.getSnapshotBeforeUpdate) || + ("function" !== typeof g.UNSAFE_componentWillReceiveProps && + "function" !== typeof g.componentWillReceiveProps) || + ((h !== A || p !== k) && Ng(b, g, d, k)); + wg = !1; + p = b.memoizedState; + g.state = p; + Cg(b, d, g, e); + var x = b.memoizedState; + h !== A || p !== x || N.current || wg + ? ("function" === typeof C && + (Gg(b, c, C, d), (x = b.memoizedState)), + (l = wg || Lg(b, c, l, d, p, x, k)) + ? (n || + ("function" !== + typeof g.UNSAFE_componentWillUpdate && + "function" !== + typeof g.componentWillUpdate) || + ("function" === typeof g.componentWillUpdate && + g.componentWillUpdate(d, x, k), + "function" === + typeof g.UNSAFE_componentWillUpdate && + g.UNSAFE_componentWillUpdate(d, x, k)), + "function" === typeof g.componentDidUpdate && + (b.flags |= 4), + "function" === typeof g.getSnapshotBeforeUpdate && + (b.flags |= 256)) + : ("function" !== typeof g.componentDidUpdate || + (h === a.memoizedProps && + p === a.memoizedState) || + (b.flags |= 4), + "function" !== typeof g.getSnapshotBeforeUpdate || + (h === a.memoizedProps && + p === a.memoizedState) || + (b.flags |= 256), + (b.memoizedProps = d), + (b.memoizedState = x)), + (g.props = d), + (g.state = x), + (g.context = k), + (d = l)) + : ("function" !== typeof g.componentDidUpdate || + (h === a.memoizedProps && p === a.memoizedState) || + (b.flags |= 4), + "function" !== typeof g.getSnapshotBeforeUpdate || + (h === a.memoizedProps && p === a.memoizedState) || + (b.flags |= 256), + (d = !1)); + } + return qi(a, b, c, d, f, e); + } + function qi(a, b, c, d, e, f) { + oi(a, b); + var g = 0 !== (b.flags & 64); + if (!d && !g) return e && Kf(b, c, !1), hi(a, b, f); + d = b.stateNode; + ei.current = b; + var h = + g && "function" !== typeof c.getDerivedStateFromError + ? null + : d.render(); + b.flags |= 1; + null !== a && g + ? ((b.child = Yg(b, a.child, null, f)), + (b.child = Yg(b, null, h, f))) + : fi(a, b, h, f); + b.memoizedState = d.state; + e && Kf(b, c, !0); + return b.child; + } + function ri(a) { + var b = a.stateNode; + b.pendingContext + ? Hf(a, b.pendingContext, b.pendingContext !== b.context) + : b.context && Hf(a, b.context, !1); + eh(a, b.containerInfo); + } + var si = { dehydrated: null, retryLane: 0 }; + function ti(a, b, c) { + var d = b.pendingProps, + e = P.current, + f = !1, + g; + (g = 0 !== (b.flags & 64)) || + (g = null !== a && null === a.memoizedState ? !1 : 0 !== (e & 2)); + g + ? ((f = !0), (b.flags &= -65)) + : (null !== a && null === a.memoizedState) || + void 0 === d.fallback || + !0 === d.unstable_avoidThisFallback || + (e |= 1); + I(P, e & 1); + if (null === a) { + void 0 !== d.fallback && ph(b); + a = d.children; + e = d.fallback; + if (f) + return ( + (a = ui(b, a, e, c)), + (b.child.memoizedState = { baseLanes: c }), + (b.memoizedState = si), + a + ); + if ("number" === typeof d.unstable_expectedLoadTime) + return ( + (a = ui(b, a, e, c)), + (b.child.memoizedState = { baseLanes: c }), + (b.memoizedState = si), + (b.lanes = 33554432), + a + ); + c = vi({ mode: "visible", children: a }, b.mode, c, null); + c.return = b; + return (b.child = c); + } + if (null !== a.memoizedState) { + if (f) + return ( + (d = wi(a, b, d.children, d.fallback, c)), + (f = b.child), + (e = a.child.memoizedState), + (f.memoizedState = + null === e + ? { baseLanes: c } + : { baseLanes: e.baseLanes | c }), + (f.childLanes = a.childLanes & ~c), + (b.memoizedState = si), + d + ); + c = xi(a, b, d.children, c); + b.memoizedState = null; + return c; + } + if (f) + return ( + (d = wi(a, b, d.children, d.fallback, c)), + (f = b.child), + (e = a.child.memoizedState), + (f.memoizedState = + null === e + ? { baseLanes: c } + : { baseLanes: e.baseLanes | c }), + (f.childLanes = a.childLanes & ~c), + (b.memoizedState = si), + d + ); + c = xi(a, b, d.children, c); + b.memoizedState = null; + return c; + } + function ui(a, b, c, d) { + var e = a.mode, + f = a.child; + b = { mode: "hidden", children: b }; + 0 === (e & 2) && null !== f + ? ((f.childLanes = 0), (f.pendingProps = b)) + : (f = vi(b, e, 0, null)); + c = Xg(c, e, d, null); + f.return = a; + c.return = a; + f.sibling = c; + a.child = f; + return c; + } + function xi(a, b, c, d) { + var e = a.child; + a = e.sibling; + c = Tg(e, { mode: "visible", children: c }); + 0 === (b.mode & 2) && (c.lanes = d); + c.return = b; + c.sibling = null; + null !== a && + ((a.nextEffect = null), + (a.flags = 8), + (b.firstEffect = b.lastEffect = a)); + return (b.child = c); + } + function wi(a, b, c, d, e) { + var f = b.mode, + g = a.child; + a = g.sibling; + var h = { mode: "hidden", children: c }; + 0 === (f & 2) && b.child !== g + ? ((c = b.child), + (c.childLanes = 0), + (c.pendingProps = h), + (g = c.lastEffect), + null !== g + ? ((b.firstEffect = c.firstEffect), + (b.lastEffect = g), + (g.nextEffect = null)) + : (b.firstEffect = b.lastEffect = null)) + : (c = Tg(g, h)); + null !== a ? (d = Tg(a, d)) : ((d = Xg(d, f, e, null)), (d.flags |= 2)); + d.return = b; + c.return = b; + c.sibling = d; + b.child = c; + return d; + } + function yi(a, b) { + a.lanes |= b; + var c = a.alternate; + null !== c && (c.lanes |= b); + sg(a.return, b); + } + function zi(a, b, c, d, e, f) { + var g = a.memoizedState; + null === g + ? (a.memoizedState = { + isBackwards: b, + rendering: null, + renderingStartTime: 0, + last: d, + tail: c, + tailMode: e, + lastEffect: f, + }) + : ((g.isBackwards = b), + (g.rendering = null), + (g.renderingStartTime = 0), + (g.last = d), + (g.tail = c), + (g.tailMode = e), + (g.lastEffect = f)); + } + function Ai(a, b, c) { + var d = b.pendingProps, + e = d.revealOrder, + f = d.tail; + fi(a, b, d.children, c); + d = P.current; + if (0 !== (d & 2)) (d = (d & 1) | 2), (b.flags |= 64); + else { + if (null !== a && 0 !== (a.flags & 64)) + a: for (a = b.child; null !== a; ) { + if (13 === a.tag) null !== a.memoizedState && yi(a, c); + else if (19 === a.tag) yi(a, c); + else if (null !== a.child) { + a.child.return = a; + a = a.child; + continue; + } + if (a === b) break a; + for (; null === a.sibling; ) { + if (null === a.return || a.return === b) break a; + a = a.return; + } + a.sibling.return = a.return; + a = a.sibling; + } + d &= 1; + } + I(P, d); + if (0 === (b.mode & 2)) b.memoizedState = null; + else + switch (e) { + case "forwards": + c = b.child; + for (e = null; null !== c; ) + (a = c.alternate), + null !== a && null === ih(a) && (e = c), + (c = c.sibling); + c = e; + null === c + ? ((e = b.child), (b.child = null)) + : ((e = c.sibling), (c.sibling = null)); + zi(b, !1, e, c, f, b.lastEffect); + break; + case "backwards": + c = null; + e = b.child; + for (b.child = null; null !== e; ) { + a = e.alternate; + if (null !== a && null === ih(a)) { + b.child = e; + break; + } + a = e.sibling; + e.sibling = c; + c = e; + e = a; + } + zi(b, !0, c, null, f, b.lastEffect); + break; + case "together": + zi(b, !1, null, null, void 0, b.lastEffect); + break; + default: + b.memoizedState = null; + } + return b.child; + } + function hi(a, b, c) { + null !== a && (b.dependencies = a.dependencies); + Dg |= b.lanes; + if (0 !== (c & b.childLanes)) { + if (null !== a && b.child !== a.child) throw Error(y(153)); + if (null !== b.child) { + a = b.child; + c = Tg(a, a.pendingProps); + b.child = c; + for (c.return = b; null !== a.sibling; ) + (a = a.sibling), + (c = c.sibling = Tg(a, a.pendingProps)), + (c.return = b); + c.sibling = null; + } + return b.child; + } + return null; + } + var Bi, Ci, Di, Ei; + Bi = function (a, b) { + for (var c = b.child; null !== c; ) { + if (5 === c.tag || 6 === c.tag) a.appendChild(c.stateNode); + else if (4 !== c.tag && null !== c.child) { + c.child.return = c; + c = c.child; + continue; + } + if (c === b) break; + for (; null === c.sibling; ) { + if (null === c.return || c.return === b) return; + c = c.return; + } + c.sibling.return = c.return; + c = c.sibling; + } + }; + Ci = function () {}; + Di = function (a, b, c, d) { + var e = a.memoizedProps; + if (e !== d) { + a = b.stateNode; + dh(ah.current); + var f = null; + switch (c) { + case "input": + e = Ya(a, e); + d = Ya(a, d); + f = []; + break; + case "option": + e = eb(a, e); + d = eb(a, d); + f = []; + break; + case "select": + e = m({}, e, { value: void 0 }); + d = m({}, d, { value: void 0 }); + f = []; + break; + case "textarea": + e = gb(a, e); + d = gb(a, d); + f = []; + break; + default: + "function" !== typeof e.onClick && + "function" === typeof d.onClick && + (a.onclick = jf); + } + vb(c, d); + var g; + c = null; + for (l in e) + if (!d.hasOwnProperty(l) && e.hasOwnProperty(l) && null != e[l]) + if ("style" === l) { + var h = e[l]; + for (g in h) + h.hasOwnProperty(g) && (c || (c = {}), (c[g] = "")); + } else + "dangerouslySetInnerHTML" !== l && + "children" !== l && + "suppressContentEditableWarning" !== l && + "suppressHydrationWarning" !== l && + "autoFocus" !== l && + (ca.hasOwnProperty(l) + ? f || (f = []) + : (f = f || []).push(l, null)); + for (l in d) { + var k = d[l]; + h = null != e ? e[l] : void 0; + if (d.hasOwnProperty(l) && k !== h && (null != k || null != h)) + if ("style" === l) + if (h) { + for (g in h) + !h.hasOwnProperty(g) || + (k && k.hasOwnProperty(g)) || + (c || (c = {}), (c[g] = "")); + for (g in k) + k.hasOwnProperty(g) && + h[g] !== k[g] && + (c || (c = {}), (c[g] = k[g])); + } else c || (f || (f = []), f.push(l, c)), (c = k); + else + "dangerouslySetInnerHTML" === l + ? ((k = k ? k.__html : void 0), + (h = h ? h.__html : void 0), + null != k && h !== k && (f = f || []).push(l, k)) + : "children" === l + ? ("string" !== typeof k && + "number" !== typeof k) || + (f = f || []).push(l, "" + k) + : "suppressContentEditableWarning" !== l && + "suppressHydrationWarning" !== l && + (ca.hasOwnProperty(l) + ? (null != k && + "onScroll" === l && + G("scroll", a), + f || h === k || (f = [])) + : "object" === typeof k && + null !== k && + k.$$typeof === Ga + ? k.toString() + : (f = f || []).push(l, k)); + } + c && (f = f || []).push("style", c); + var l = f; + if ((b.updateQueue = l)) b.flags |= 4; + } + }; + Ei = function (a, b, c, d) { + c !== d && (b.flags |= 4); + }; + function Fi(a, b) { + if (!lh) + switch (a.tailMode) { + case "hidden": + b = a.tail; + for (var c = null; null !== b; ) + null !== b.alternate && (c = b), (b = b.sibling); + null === c ? (a.tail = null) : (c.sibling = null); + break; + case "collapsed": + c = a.tail; + for (var d = null; null !== c; ) + null !== c.alternate && (d = c), (c = c.sibling); + null === d + ? b || null === a.tail + ? (a.tail = null) + : (a.tail.sibling = null) + : (d.sibling = null); + } + } + function Gi(a, b, c) { + var d = b.pendingProps; + switch (b.tag) { + case 2: + case 16: + case 15: + case 0: + case 11: + case 7: + case 8: + case 12: + case 9: + case 14: + return null; + case 1: + return Ff(b.type) && Gf(), null; + case 3: + fh(); + H(N); + H(M); + uh(); + d = b.stateNode; + d.pendingContext && + ((d.context = d.pendingContext), (d.pendingContext = null)); + if (null === a || null === a.child) + rh(b) ? (b.flags |= 4) : d.hydrate || (b.flags |= 256); + Ci(b); + return null; + case 5: + hh(b); + var e = dh(ch.current); + c = b.type; + if (null !== a && null != b.stateNode) + Di(a, b, c, d, e), a.ref !== b.ref && (b.flags |= 128); + else { + if (!d) { + if (null === b.stateNode) throw Error(y(166)); + return null; + } + a = dh(ah.current); + if (rh(b)) { + d = b.stateNode; + c = b.type; + var f = b.memoizedProps; + d[wf] = b; + d[xf] = f; + switch (c) { + case "dialog": + G("cancel", d); + G("close", d); + break; + case "iframe": + case "object": + case "embed": + G("load", d); + break; + case "video": + case "audio": + for (a = 0; a < Xe.length; a++) G(Xe[a], d); + break; + case "source": + G("error", d); + break; + case "img": + case "image": + case "link": + G("error", d); + G("load", d); + break; + case "details": + G("toggle", d); + break; + case "input": + Za(d, f); + G("invalid", d); + break; + case "select": + d._wrapperState = { wasMultiple: !!f.multiple }; + G("invalid", d); + break; + case "textarea": + hb(d, f), G("invalid", d); + } + vb(c, f); + a = null; + for (var g in f) + f.hasOwnProperty(g) && + ((e = f[g]), + "children" === g + ? "string" === typeof e + ? d.textContent !== e && + (a = ["children", e]) + : "number" === typeof e && + d.textContent !== "" + e && + (a = ["children", "" + e]) + : ca.hasOwnProperty(g) && + null != e && + "onScroll" === g && + G("scroll", d)); + switch (c) { + case "input": + Va(d); + cb(d, f, !0); + break; + case "textarea": + Va(d); + jb(d); + break; + case "select": + case "option": + break; + default: + "function" === typeof f.onClick && + (d.onclick = jf); + } + d = a; + b.updateQueue = d; + null !== d && (b.flags |= 4); + } else { + g = 9 === e.nodeType ? e : e.ownerDocument; + a === kb.html && (a = lb(c)); + a === kb.html + ? "script" === c + ? ((a = g.createElement("div")), + (a.innerHTML = "