diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 00000000..c4dd9488 --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,32 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - main + +jobs: + deploy: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install dependencies in apps/liveviewjs.com + run: npm install -w apps/liveviewjs.com + - name: Build docusaurus website + run: npm run build -w apps/liveviewjs.com + + # Popular action to deploy to GitHub Pages: + # Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # Build output to publish to the `gh-pages` branch: + publish_dir: ./apps/liveviewjs.com/build + user_name: floodfx + user_email: donnie@floodfx.com diff --git a/.github/workflows/test-docs-deploy.yml b/.github/workflows/test-docs-deploy.yml new file mode 100644 index 00000000..8dbee430 --- /dev/null +++ b/.github/workflows/test-docs-deploy.yml @@ -0,0 +1,23 @@ +name: Test Docs Deploy + +on: + push: + branches: + - main + pull_request: + branches: + - "*" + +jobs: + test-deploy: + name: Test Docs Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies in apps/liveviewjs.com + run: npm install -w apps/liveviewjs.com + - name: Build docusaurus website + run: npm run build -w apps/liveviewjs.com diff --git a/apps/liveviewjs.com/docs/01-overview/gratitude.md b/apps/liveviewjs.com/docs/01-overview/gratitude.md index b1067466..c4526e5a 100644 --- a/apps/liveviewjs.com/docs/01-overview/gratitude.md +++ b/apps/liveviewjs.com/docs/01-overview/gratitude.md @@ -11,3 +11,5 @@ we can reuse on the client side. client code instead of reinventing! 🙌 Thanks to [@blimmer](https://github.com/blimmer/) for the awesome feedback, documentation suggestions, and support! + +Thanks to you for checking out **LiveViewJS**! We hope you enjoy it as much as we do! diff --git a/apps/liveviewjs.com/docs/01-overview/introduction.md b/apps/liveviewjs.com/docs/01-overview/introduction.md index af59a3ca..7207eb3b 100644 --- a/apps/liveviewjs.com/docs/01-overview/introduction.md +++ b/apps/liveviewjs.com/docs/01-overview/introduction.md @@ -11,12 +11,31 @@ far less code and complexity and far more speed and efficiency**. ## What is a LiveView? -A LiveView is a server-rendered HTML page that connects to the server via a persistent web socket. The web socket allows -the client to send user events to the server and the server to send diffs in response. **LiveViewJS** is a LiveView -framework that handles the complex part of this (handling websockets, diffing changes, applying DOM updates, etc.) so that -you can focus on building your application. +The LiveView pattern, [as popularized in Elixir’s Phoenix framework](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html), shifts your UI’s state management, event handling to the server, calculating minimal diffs to drive updates in your HTML over WebSockets. -Here's the typical "counter" example written as a LiveView in **LiveViewJS**: +There are many benefits to this approach, including: + * extremely fast, SEO-friendly page loads + * simple state management that works well at any scale (not just small apps) + * minimal data transfer (only send diffs) + * no need to build (or operate) REST or GraphQL APIs + +There are also some drawbacks, including: + * requires a server (e.g. pure static sites are not the best fit) + * requires a internet connection (e.g. offline-first apps are not the best fit - though sporadic internet connectivity is fine) + +If you are building a web-based application, the LiveView approach is a super-powerful, some say "game-changing", way to build it and LiveViewJS is the best way to do it in NodeJS and Deno. + +### More detail + +As mentioned, a LiveView is a server-rendered HTML page that, when loaded, connects back to the server via a persistent web socket. As the user interacts with the LiveView, the client to sends user events (click, keys, etc) via the websocket back to the server and the server responds with diffs to the HTML page in return. + +### Websockets and Diffs? That sounds complicated! + +As a developer, you don't need to worry about connecting websockets or calculating diffs. **LiveViewJS** handles all of the complex parts of this for you. You just focus on implementing your business logic and rendering your views and LiveViewJS handles the rest. + +## Example "Counter" LiveView + +Here's the typical "counter" in **LiveViewJS**: ```ts import { createLiveView, html } from "liveviewjs"; @@ -24,10 +43,7 @@ import { createLiveView, html } from "liveviewjs"; /** * A basic counter that increments and decrements a number. */ -export const counterLiveView = createLiveView< - { count: number }, // Define LiveView Context (a.k.a state) - { type: "increment" } | { type: "decrement" } // Define LiveView Events ->({ +export const counterLiveView = createLiveView({ mount: (socket) => { // init state, set count to 0 socket.assign({ count: 0 }); @@ -66,16 +82,20 @@ Yes, it "looks" like a React/Vue/Svelt UI but the main differences are: - This page was first rendered as plain HTML (not a bundle of JS) - The client is automatically connected to a server via a websocket - The click events are automatically shipped to the server -- The server then runs the business logic to update the state -- Using the new state, the server then renders a new view and calculates any diffs -- Those diffs are shipped back to the client, where they are automatically applied +- The server then runs the business logic to update the state +- The server automatically calculates the minimal diffs and sends them to the client +- The client automatically applies the diffs to the DOM Pretty cool eh? We think so too! While this counter LiveView isn't particularly useful, it gives you a quick intro to how -LiveViews work and what they look like both as code and in the browser. We've got a lot more to show you about -**LiveViewJS** including: built-in real-time / multi-player support, built-in form validation with changesets, built-in -file uploads with image previews and drag and drop support, and more! +LiveViews work and what they look like both as code and in the browser. -But first, a bit more about LiveViews... +We've got a lot more to show you about **LiveViewJS** including some amazing features built-in the framework like: + + * real-time / multi-player support + * simple, powerfil form validation with changesets + * file uploads with image previews and drag and drop support, and more! + +If you want more detail on LiveViews and how awesome they are feel free to keep reading. If you want to jump right in, check out the [Quick Start](../02-quick-starts/get-liveviewjs-repo.md) guide. ## LiveView is already proven technology @@ -115,9 +135,6 @@ Here is a quote from the author and inventor of LiveView, [Chris McCord](http:// - **No client-side routing** - No need to use a library like React Router or Vue Router. LiveViews route like multi-page applications which is handled automatically by the browser and server. (Another major complication rendered unnecessary.) -- **No component libraries (or Storybook) required** - Traditional SPAs require teams to adopt, build or buy and then - maintain, and customize a component library and use something like [Storybook](https://storybook.js.org/). LiveView - simplifies this greatly with server-side HTML templates. ## Disadvantages @@ -134,11 +151,3 @@ and productivity of LiveView to the NodeJS and Deno ecosystems** and are obvious team that invented it. We believe in it so much that we think more developers should have access to the programming paradigm it enables. -### Reach more developers - -Unfortunately, Elixir only represents -[about 2%](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-programming-scripting-and-markup-languages) -of the programming language market share. We believe that **LiveViewJS** will help bring the productivity and magic of -LiveView to the -[65% of developers](https://survey.stackoverflow.co/2022/#section-most-popular-technologies-programming-scripting-and-markup-languages) -that use Javascript (and Typescript). diff --git a/apps/liveviewjs.com/docs/01-overview/runtimes.md b/apps/liveviewjs.com/docs/01-overview/runtimes.md index 109ad7a4..18199c9d 100644 --- a/apps/liveviewjs.com/docs/01-overview/runtimes.md +++ b/apps/liveviewjs.com/docs/01-overview/runtimes.md @@ -40,10 +40,10 @@ LiveViewJS is broken up into the following packages: - `liveviewjs` [Node](https://www.npmjs.com/package/liveviewjs) and [Deno](https://deno.land/x/liveviewjs) - The core package that contains the **LiveViewJS** core server code. This package is runtime agnostic and can be used with either NodeJS or Deno. +- `@liveviewjs/gen` [Link](https://www.npmjs.com/package/@liveviewjs/gen) - The code generator package that is used to + generate the LiveViewJS code including minimal project configuration for both NodeJS and Deno. This package will also add support for generating LiveViews and LiveComponents as well as other boilerplate code. +- `@liveviewjs/express` [Node](https://www.npmjs.com/package/@liveviewjs/express) - The Express package contains [ExpressJS](https://expressjs.com/) integration code and NodeJS-specific utilities like Redis pub/sub. This package is used to integrate **LiveViewJS** with Express (NodeJS). To user this packages install via `npm install @liveviewjs/express` or `yarn add @liveviewjs/express`. +- `deno` [Link](https://github.com/floodfx/liveviewjs/tree/main/packages/deno) - The Deno package contains [Oak](https://deno.land/x/oak) integration code and Deno-specific LiveViewJS utilities like BroadcastChannel pub/sub. This package is used to integrate **LiveViewJS** with Oak (Deno). To use this package, import into your Deno project via `import { LiveViewJS } from "https://raw.githubusercontent.com/floodfx/liveviewjs/main/packages/deno/mod.ts";`. (Note: DenoLand is broken for mono-repos, so we're hosting the package on GitHub for now.) - `@liveviewjs/examples` [Node](https://www.npmjs.com/package/@liveviewjs/examples) or - [Deno](https://deno.land/x/liveviewjs/packages/examples/mod.ts) - The package contains all the example LiveViews that - can be run on either NodeJS or Deno. This package is runtime agnostic and can be used with either NodeJS or Deno. -- `@liveviewjs/express` [Node](https://www.npmjs.com/package/@liveviewjs/express) - The Express package that contains - the Express server integration code. This package is used to integrate **LiveViewJS** with Express (NodeJS). -- `https://deno.land/x/liveviewjs@VERSION/packages/deno/mod.ts` - The Deno package that contains the Oak server - integration code. This package is used to integrate **LiveViewJS** with Oak (Deno). + [Deno](https://raw.githubusercontent.com/floodfx/liveviewjs/main/packages/examples/mod.ts) - The package contains all the example LiveViews that + can be run on either NodeJS or Deno. This package is runtime agnostic and can be used with either NodeJS or Deno. See the [examples](https://github.com/floodfx/liveviewjs/tree/main/packages/examples/src/liveviews) directory for the source code. diff --git a/apps/liveviewjs.com/docs/02-quick-starts/get-liveviewjs-repo.md b/apps/liveviewjs.com/docs/02-quick-starts/get-liveviewjs-repo.md index 8324d3b9..dccd2f73 100644 --- a/apps/liveviewjs.com/docs/02-quick-starts/get-liveviewjs-repo.md +++ b/apps/liveviewjs.com/docs/02-quick-starts/get-liveviewjs-repo.md @@ -2,6 +2,22 @@ sidebar_position: 1 --- +# Let's Build a LiveView + +Building a LiveView is easy with **LiveViewJS**. You can get started in just a few minutes. + +## Create a New Project + +**LiveViewJS** has a project generation tool that will setup the project structure and install the required dependencies for either NodeJS or Deno. + +Run: +```bash +npx @liveviewjs/gen +``` + +You will be prompted to select the type of project you want to create and a couple other questions. Then, voilà, you will have a new project that runs out of the box! + + # Download the Repo The fastest way to run the example or build your own LiveView is by downloading the **LiveViewJS** repo. This repo diff --git a/apps/liveviewjs.com/package.json b/apps/liveviewjs.com/package.json index 87742ee1..dff9be9d 100644 --- a/apps/liveviewjs.com/package.json +++ b/apps/liveviewjs.com/package.json @@ -5,7 +5,7 @@ "scripts": { "docusaurus": "docusaurus", "start": "docusaurus start", - "build": "docusaurus build && rm -rf ../../docs && mv build ../../docs", + "build": "docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index 6e9eb7c4..00000000 --- a/docs/404.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Page Not Found | LiveViewJS - - - - -
-
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - - - \ No newline at end of file diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index d5fec36f..00000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -www.liveviewjs.com diff --git a/docs/assets/css/styles.02541653.css b/docs/assets/css/styles.02541653.css deleted file mode 100644 index 37617062..00000000 --- a/docs/assets/css/styles.02541653.css +++ /dev/null @@ -1 +0,0 @@ -.col,.container{padding:0 var(--ifm-spacing-horizontal)}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.toggleButton_wYmb,html{-webkit-tap-highlight-color:transparent}.clean-list,.containsTaskList__YnT,.details_B4FW>summary,.dropdown__menu,.menu__list{list-style:none}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:transparent;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:rgba(0,0,0,.05);--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 rgba(0,0,0,.1);--ifm-global-shadow-md:0 5px 40px rgba(0,0,0,.2);--ifm-global-shadow-tl:0 12px 28px 0 rgba(0,0,0,.2),0 2px 4px 0 rgba(0,0,0,.1);--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:transparent;--ifm-table-stripe-background:rgba(0,0,0,.03);--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-color-primary:#0cafba;--ifm-color-primary-dark:#0cafba;--ifm-color-primary-darker:#0cafba;--ifm-color-primary-darkest:#0cafba;--ifm-color-primary-light:#0cafba;--ifm-color-primary-lighter:#0cafba;--ifm-color-primary-lightest:#0cafba;--ifm-code-font-size:95%;--docusaurus-highlighted-code-line-bg:rgba(0,0,0,.1);--docusaurus-announcement-bar-height:auto;--docusaurus-collapse-button-bg:transparent;--docusaurus-collapse-button-bg-hover:rgba(0,0,0,.1);--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px;--docusaurus-tag-list-border:var(--ifm-color-emphasis-300)}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:transparent}*{box-sizing:border-box}html{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base)}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.list_iQEt article:last-child,.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_xK2O,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width);width:100%}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid rgba(0,0,0,.1);border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:transparent;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_vR9E .wordWrapButtonIcon_SMj9{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_l909,.alert__heading,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:rgba(53,120,229,.15);--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:rgba(235,237,240,.15);--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:rgba(0,164,0,.15);--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:rgba(84,199,236,.15);--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:rgba(255,186,0,.15);--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:rgba(250,56,62,.15);--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{-webkit-text-decoration-color:var(--ifm-alert-border-color);text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.card--full-height,.navbar__logo img,body,html{height:100%}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area.breadcrumbs__link[href]:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:-webkit-any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:transparent;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_YxQB:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.sidebar_RYHo,.tableOfContents_TN1Q{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor transparent;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.docItemContainer_Rv5Z article>:first-child,.docItemContainer_Rv5Z header+*,.footer__item{margin-top:0}.admonitionContent_oUmQ>:last-child,.cardContainer_Shn5 :last-child,.collapsibleContent_VYua>:last-child,.footer__items{margin-bottom:0}.codeBlockStandalone_K9VJ,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title,.title_cIQJ{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_B4FW[data-collapsed=false].isBrowser_Cof9>summary:before,.details_B4FW[open]:not(.isBrowser_Cof9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{content:"";height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;filter:var(--ifm-menu-link-sublist-icon-filter)}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.docsWrapper_BqXd,.flex,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:-webkit-sticky;position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;position:fixed;transition-timing-function:ease-in-out;left:0;top:0;visibility:hidden}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:hsla(0,0%,100%,.1);--ifm-navbar-search-input-placeholder-color:hsla(0,0%,100%,.5);color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:hsla(0,0%,100%,.05);--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{-webkit-appearance:none;appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:rgba(0,0,0,.6);right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover,.sidebarItemLink_EKgd:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__link--next{grid-column:2/3;text-align:right}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs,:not(.containsTaskList__YnT>li)>.containsTaskList__YnT{padding-left:0}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto}.tabs__item{border-bottom:3px solid transparent;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:hsla(0,0%,100%,.05);--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:hsla(0,0%,100%,.1);--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:hsla(0,0%,100%,.07);--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}.container{width:100%}.mt-4{margin-top:1rem}.grid{display:grid}.w-3\/4{width:75%}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.justify-center{justify-content:center}.gap-y-10{row-gap:2.5rem}.bg-brand{background-color:#0cafba}.py-8{padding-bottom:2rem;padding-top:2rem}.text-center{text-align:center}.font-brand,.navbar__title,h1,h2,h3,h4{font-family:LibreFranklin,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.font-semibold{font-weight:600}.font-thin{font-weight:100}.text-brand{color:#0cafba}@font-face{font-display:swap;font-family:LibreFranklin;font-style:normal;src:url(/assets/fonts/LibreFranklin-VariableFont_wght-36548e4c1ed430cc732754e5f4d9c5bb.ttf) format("truetype")}@font-face{font-display:swap;font-family:LibreFranklin;font-style:italic;src:url(/assets/fonts/LibreFranklin-Italic-VariableFont_wght-607905701bd37ee4f8fcf7b08571bcae.ttf) format("truetype")}.navbar__title{color:#0cafba;font-size:2rem;font-style:normal;font-weight:medium;letter-spacing:.05em;line-height:1.5}[data-theme=dark]{--ifm-color-primary:#0cafba;--ifm-color-primary-dark:#0cafba;--ifm-color-primary-darker:#0cafba;--ifm-color-primary-darkest:#0cafba;--ifm-color-primary-light:#0cafba;--ifm-color-primary-lighter:#0cafba;--ifm-color-primary-lightest:#0cafba;--docusaurus-highlighted-code-line-bg:rgba(0,0,0,.3)}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#docusaurus-base-url-issue-banner-container,.collapseSidebarButton_Ftvb,.docSidebarContainer_aIKW,.sidebarLogo_nlll,.themedImage_RWGG,[data-theme=dark] .lightToggleIcon_Sxwe,[data-theme=light] .darkToggleIcon_Yem1,html[data-announcement-bar-initially-dismissed=true] .announcementBar_ncOr{display:none}.skipToContent_G6ar{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_G6ar:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.announcementBar_ncOr{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_ajMw{flex:0 0 10px}.announcementBarClose_c9u4{align-self:stretch;flex:0 0 30px;line-height:0;padding:0}.announcementBarContent__57G{flex:1 1 auto;font-size:85%;padding:5px 0;text-align:center}.announcementBarContent__57G a{color:inherit;text-decoration:underline}.toggle_OLSw{height:2rem;width:2rem}.toggleButton_wYmb{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_wYmb:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_vaDU{cursor:not-allowed}[data-theme=dark] .themedImage--dark_Dsi0,[data-theme=light] .themedImage--light_riBm{display:initial}.iconExternalLink_awgD{margin-left:.3rem}.iconLanguage_xrmG{margin-right:5px;vertical-align:text-bottom}.navbarHideable_bChn{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_zsXl{transform:translate3d(0,calc(-100% - 2px),0)}.footerLogoLink_zxYv{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.footerLogoLink_zxYv:hover,.hash-link:focus,:hover>.hash-link{opacity:1}.mainWrapper_UyTV{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.sidebar_RYHo{overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 2rem)}.sidebarItemTitle_sRjx{font-size:var(--ifm-h3-font-size);font-weight:var(--ifm-font-weight-bold)}.container_PuMg,.sidebarItemList_uMtB{font-size:.9rem}.sidebarItem_rygH{margin-top:.7rem}.sidebarItemLink_EKgd{color:var(--ifm-font-color-base);display:block}.sidebarItemLinkActive_hRXJ{color:var(--ifm-color-primary)!important}.cardContainer_Shn5{--ifm-link-color:var(--ifm-color-emphasis-800);--ifm-link-hover-color:var(--ifm-color-emphasis-700);--ifm-link-hover-decoration:none;border:1px solid var(--ifm-color-emphasis-200);box-shadow:0 1.5px 3px 0 rgba(0,0,0,.15);transition:all var(--ifm-transition-fast) ease;transition-property:border,box-shadow}.cardContainer_Shn5:hover{border-color:var(--ifm-color-primary);box-shadow:0 3px 6px 0 rgba(0,0,0,.2)}.cardTitle_h48N{font-size:1.2rem}.cardDescription_CytT{font-size:.8rem}.backToTopButton_z1FD{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.buttonGroup_TNwR button,.codeBlockContainer_ZGJx{background:var(--prism-background-color);color:var(--prism-color)}.backToTopButton_z1FD:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_w1wE{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:hsla(0,0%,100%,.05);--docusaurus-collapse-button-bg-hover:hsla(0,0%,100%,.1)}.docMainContainer_fv3b,.docPage_pOTq{display:flex;width:100%}.features_t9lD{align-items:center;display:flex;padding:2rem 0;width:100%}.featureSvg_GfXr{height:200px;width:200px}.heroBanner_qdFl{overflow:hidden;padding:4rem 0;position:relative;text-align:center}.buttons_AeoN{align-items:center;display:flex;justify-content:center}.authorCol_q_iI{flex-grow:1!important;max-width:inherit!important}.imageOnlyAuthorRow_les7{display:flex;flex-flow:row wrap}.imageOnlyAuthorCol_uMKf{margin-left:.3rem;margin-right:.3rem}.codeBlockContainer_ZGJx{border-radius:var(--ifm-code-border-radius);box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading)}.codeBlockContent_qZBB{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_zAEH{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_TAPP{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockTitle_zAEH+.codeBlockContent_qZBB .codeBlock_TAPP{border-top-left-radius:0;border-top-right-radius:0}.codeBlockLines_AdAo{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_p5De{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup_TNwR{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup_TNwR button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity .2s ease-in-out}.buttonGroup_TNwR button:focus-visible,.buttonGroup_TNwR button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup_TNwR button{opacity:.4}.iconEdit_UohW{margin-right:.3em;vertical-align:sub}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_DPDv{counter-increment:a;display:table-row}.codeLineNumber_YxQB{background:var(--ifm-pre-background);display:table-cell;left:0;padding:0 var(--ifm-pre-padding);position:-webkit-sticky;position:sticky;text-align:right;width:1%}.codeLineNumber_YxQB:before{content:counter(a);opacity:.4}.codeLineContent_SOIp{padding-right:var(--ifm-pre-padding)}.tag_qE9H{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_qE9H:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_aHXt{border-radius:.5rem;font-size:90%;padding:.3rem .5rem}.tagWithCount_UC8q{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_UC8q:after,.tagWithCount_UC8q:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_UC8q:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_UC8q:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_UC8q span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tag_YYDp{display:inline-block;margin:.5rem .5rem 0 1rem}.theme-code-block:hover .copyButtonCopied_Mzdr{opacity:1!important}.copyButtonIcons_MVhB{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_yxgH,.copyButtonSuccessIcon_QJLJ{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:.15s;width:inherit}.copyButtonSuccessIcon_QJLJ{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_Mzdr .copyButtonIcon_yxgH{opacity:0;transform:scale(.33)}.copyButtonCopied_Mzdr .copyButtonSuccessIcon_QJLJ{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.tags_q74f{display:inline}.tag_lSC7{display:inline-block;margin:0 .4rem .5rem 0}.lastUpdated_T23F{font-size:smaller;font-style:italic;margin-top:.2rem}.tocCollapsibleButton_htYj{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_htYj:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_pAh7:after,.tocCollapsibleExpanded_klrc{transform:none}.tocCollapsible_O_Qc{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleContent_SlnY>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_SlnY ul li{margin:.4rem .8rem}.tocCollapsibleContent_SlnY a{display:block}.wordWrapButtonIcon_SMj9{height:1.2rem;width:1.2rem}.details_B4FW{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_B4FW>summary{cursor:pointer;padding-left:1rem;position:relative}.details_B4FW>summary::-webkit-details-marker{display:none}.details_B4FW>summary:before{border-color:transparent transparent transparent var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_VYua{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_SZgV{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.anchorWithStickyNavbar_fF9Z{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_Yh18{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.hash-link:before{content:"#"}.img__Ss2{height:auto}.admonition_qNG0{margin-bottom:1em}.admonitionHeading_l909{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.3rem}.admonitionHeading_l909 code{text-transform:none}.admonitionIcon_UNbs{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_UNbs svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.blogPostFooterDetailsFull_bikM{flex-direction:column}.tableOfContents_TN1Q{overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.breadcrumbsContainer_zCmv{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.breadcrumbHomeIcon_tMMf{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.title_AulG{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-leading)*1.25)}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}.md\:w-full{width:100%}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:gap-x-12{column-gap:3rem}}@media (min-width:997px){.collapseSidebarButton_Ftvb,.expandButton_qIqc{background-color:var(--docusaurus-collapse-button-bg)}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_c9u4,.announcementBarPlaceholder_ajMw{flex-basis:50px}.searchBox_xrOJ{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.collapseSidebarButton_Ftvb{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:-webkit-sticky;position:sticky}.collapseSidebarButtonIcon_c4WT{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_aOpf,[dir=rtl] .collapseSidebarButtonIcon_c4WT{transform:rotate(0)}.collapseSidebarButton_Ftvb:focus,.collapseSidebarButton_Ftvb:hover,.expandButton_qIqc:focus,.expandButton_qIqc:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_anEq{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_qiME{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_qiME{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_hRfJ{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_vJCc{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:-webkit-sticky;position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_Fo4g{padding-top:0}.sidebarHidden_vBKa{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_nlll{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_nlll img{height:2rem;margin-right:.5rem}.expandButton_qIqc{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;position:-webkit-sticky;position:sticky;top:0;transition:background-color var(--ifm-transition-fast) ease}[dir=rtl] .expandButtonIcon_aOpf{transform:rotate(180deg)}.docSidebarContainer_aIKW{border-right:1px solid var(--ifm-toc-border-color);-webkit-clip-path:inset(0);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_UIq3{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.docMainContainer_fv3b{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_wOQt{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_DUiu{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.lastUpdated_T23F{text-align:right}.tocMobile_tjDr{display:none}.docItemCol_YAwJ,.generatedIndexPage_ak01{max-width:75%!important}.list_iQEt article:nth-last-child(-n+2){margin-bottom:0!important}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (min-width:1536px){.container{max-width:1536px}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_Hg9V,.footer__link-separator,.navbar__item,.sidebar_RYHo,.tableOfContents_TN1Q{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.searchBox_xrOJ{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_JtJJ{padding:0 .3rem}}@media screen and (max-width:996px){.heroBanner_qdFl{padding:2rem}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}.title_cIQJ{font-size:2rem}}@media (hover:hover){.backToTopButton_z1FD:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media print{.announcementBar_ncOr,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_tjDr{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_AdAo{white-space:pre-wrap}} \ No newline at end of file diff --git a/docs/assets/fonts/LibreFranklin-Italic-VariableFont_wght-607905701bd37ee4f8fcf7b08571bcae.ttf b/docs/assets/fonts/LibreFranklin-Italic-VariableFont_wght-607905701bd37ee4f8fcf7b08571bcae.ttf deleted file mode 100644 index ccb93cc9..00000000 Binary files a/docs/assets/fonts/LibreFranklin-Italic-VariableFont_wght-607905701bd37ee4f8fcf7b08571bcae.ttf and /dev/null differ diff --git a/docs/assets/fonts/LibreFranklin-VariableFont_wght-36548e4c1ed430cc732754e5f4d9c5bb.ttf b/docs/assets/fonts/LibreFranklin-VariableFont_wght-36548e4c1ed430cc732754e5f4d9c5bb.ttf deleted file mode 100644 index a0b76eb2..00000000 Binary files a/docs/assets/fonts/LibreFranklin-VariableFont_wght-36548e4c1ed430cc732754e5f4d9c5bb.ttf and /dev/null differ diff --git a/docs/assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg b/docs/assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg deleted file mode 100644 index 11bda092..00000000 Binary files a/docs/assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg and /dev/null differ diff --git a/docs/assets/images/liveview-lifecycle-heartbeat-65d664e1b997bbc0737aee2d81e0575e.svg b/docs/assets/images/liveview-lifecycle-heartbeat-65d664e1b997bbc0737aee2d81e0575e.svg deleted file mode 100644 index d8a06b87..00000000 --- a/docs/assets/images/liveview-lifecycle-heartbeat-65d664e1b997bbc0737aee2d81e0575e.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewAutomatic Health ChecksLiveView automatically sends heartbeat messages to ensure client and server are connectedalt[1. Connection Healthy][2. Connection Lost]loop[Heartbeat]Heartbeat Ping (30 sec)1ACK2No response3Automatic Retry4Automatic Recover5ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/assets/images/liveview-lifecycle-http-phase-ea214711543a70a25c10e3bf28ab347b.svg b/docs/assets/images/liveview-lifecycle-http-phase-ea214711543a70a25c10e3bf28ab347b.svg deleted file mode 100644 index 68e9ba7a..00000000 --- a/docs/assets/images/liveview-lifecycle-http-phase-ea214711543a70a25c10e3bf28ab347b.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewHTTP Request PhaseThe initial phase is rendering the LiveView as HTMLAdvantages:- "First paint" is extremely fast (it's just HTML)- No waiting for MBs of JS to download- Search engine friendly (again it is only HTML)- Renders if JS is disabledInitialize and Render:call `mount`, `handleParams`, and `render`Wrap specific LiveView HTML with common page HTMLClient receives fully rendered LiveView HTMLHTTP RequestGET '/my-liveview'1Route to LiveView'/my-liveview => MyLiveView'2`mount`3`handleParams`4`render`5LiveView HTML<div>...</div>6Page HTML7HTTP Response<html>...</html>8ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/assets/images/liveview-lifecycle-shutdown-7e64e2d957894e06dadbde8ebfce6dc0.svg b/docs/assets/images/liveview-lifecycle-shutdown-7e64e2d957894e06dadbde8ebfce6dc0.svg deleted file mode 100644 index 1ab40852..00000000 --- a/docs/assets/images/liveview-lifecycle-shutdown-7e64e2d957894e06dadbde8ebfce6dc0.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewShutdownWhen user leaves the page or connection is lost and irrecoverable, LiveViewJS "cleans up" any resources for that sessionRelease temporary (in memory) datashutdown any intervals, etcRelease temporary (in memory) datashutdown any intervals, etcalt[User Leaves Page][Connection Irrecoverable]opt[Shutdown]User leaves page1Clean Up LiveView2LiveView Resources Released3No Heartbeat Response4Clean Up LiveView5LiveView Resources Released6ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/assets/images/liveview-lifecycle-user-and-server-events-0c078ec9c402dedb80571d075a41922e.svg b/docs/assets/images/liveview-lifecycle-user-and-server-events-0c078ec9c402dedb80571d075a41922e.svg deleted file mode 100644 index a76a0a42..00000000 --- a/docs/assets/images/liveview-lifecycle-user-and-server-events-0c078ec9c402dedb80571d075a41922e.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewInteractiveThe LiveView is interactive and can respond to user events or update based on server eventsAll user events follow the same lifecycle process. Route event to `handleEvent` and then `render` and calculate the Dynamics Tree DiffHandle event:call `handleEvent` then `render``handleEvent` may kick off additionalserver events (i.e. `sendInfo`) but thoseevents are asynchronous to this event loopLiveViewJS calculates differences betweenprevious Dynamics and current Dynamicsand ships back only changesSince the Static part of the HTML never changes, LiveView only sends back any "dynamic" that may have changed based on the eventopt[User Events]Server Events:LiveViewJS automatically routes server eventsfrom `socket.sendInfo` or Pub/Subsubscriptions to `handleInfo` to the correctLiveView instanceLiveViewJS calculates differences betweenprevious Dynamics and current Dynamicsand ships back only changesSince the Static part of the HTML never changes, LiveView only sends back any "dynamic" that may have changed based on the eventopt[Server Events]phx-click, phx-submit,phx-blur, phx-keydown, etc1Route to LiveView2`handleEvent`3`render`4LiveView HTML<div>...</div>5Calculate Dynamics Tree Diff6Dynamics Tree Patch7Route Server Events8`handleInfo`9`render`10LiveView HTML<div>...</div>11Calculate Dynamics Tree Diff12Dynamics Tree Patch13ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/assets/images/liveview-lifecycle-websocket-join-a167e1df602ab471b5bba9187ccf6ae2.svg b/docs/assets/images/liveview-lifecycle-websocket-join-a167e1df602ab471b5bba9187ccf6ae2.svg deleted file mode 100644 index bc94732d..00000000 --- a/docs/assets/images/liveview-lifecycle-websocket-join-a167e1df602ab471b5bba9187ccf6ae2.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewWebsocket Join:Establish the websocket connection and run the initial LiveView lifecycle methodsInitialize and Render:call `mount`, `handleParams`, and `render`Statics/Dynamics Tree intelligentlytracks parts of HTML that won't change (statics)and those that can (dynamics).This data structure enables optimized diffsto be shipped back to the client.Websocket is established, Statics/Dynamics Treesetup and LiveView is ready for interactive phase.Websocket Connect1Route to LiveView2`mount`3`handleParams`4`render`5LiveView HTML<div>...</div>6Build Statics/Dynamics Tree7Full Statics/Dynamics Tree8ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/assets/images/liveviewjs_counter_liveview_rec-e37b9d6de9defd8db8053c898ef0a326.gif b/docs/assets/images/liveviewjs_counter_liveview_rec-e37b9d6de9defd8db8053c898ef0a326.gif deleted file mode 100644 index f7e99a2d..00000000 Binary files a/docs/assets/images/liveviewjs_counter_liveview_rec-e37b9d6de9defd8db8053c898ef0a326.gif and /dev/null differ diff --git a/docs/assets/images/liveviewjs_examples_rec-2cc096a7ff8f675e44f0a7fa0c1108c7.gif b/docs/assets/images/liveviewjs_examples_rec-2cc096a7ff8f675e44f0a7fa0c1108c7.gif deleted file mode 100644 index 41d53712..00000000 Binary files a/docs/assets/images/liveviewjs_examples_rec-2cc096a7ff8f675e44f0a7fa0c1108c7.gif and /dev/null differ diff --git a/docs/assets/images/liveviewjs_hello_liveview-09cb9e9d8588fb31e92cbefd4d4d6ddf.png b/docs/assets/images/liveviewjs_hello_liveview-09cb9e9d8588fb31e92cbefd4d4d6ddf.png deleted file mode 100644 index c99a9ad4..00000000 Binary files a/docs/assets/images/liveviewjs_hello_liveview-09cb9e9d8588fb31e92cbefd4d4d6ddf.png and /dev/null differ diff --git a/docs/assets/images/liveviewjs_hello_liveview_deno-0f12c470d6af8952a736434974b93bfd.png b/docs/assets/images/liveviewjs_hello_liveview_deno-0f12c470d6af8952a736434974b93bfd.png deleted file mode 100644 index 53d5bb1f..00000000 Binary files a/docs/assets/images/liveviewjs_hello_liveview_deno-0f12c470d6af8952a736434974b93bfd.png and /dev/null differ diff --git a/docs/assets/images/liveviewjs_hello_toggle_liveview_deno_rec-6770b13c7109764fe416a368f712f377.gif b/docs/assets/images/liveviewjs_hello_toggle_liveview_deno_rec-6770b13c7109764fe416a368f712f377.gif deleted file mode 100644 index a5d9ebbb..00000000 Binary files a/docs/assets/images/liveviewjs_hello_toggle_liveview_deno_rec-6770b13c7109764fe416a368f712f377.gif and /dev/null differ diff --git a/docs/assets/images/liveviewjs_hello_toggle_liveview_rec-dff5867dec6513c570aed325880c032f.gif b/docs/assets/images/liveviewjs_hello_toggle_liveview_rec-dff5867dec6513c570aed325880c032f.gif deleted file mode 100644 index bd1ab383..00000000 Binary files a/docs/assets/images/liveviewjs_hello_toggle_liveview_rec-dff5867dec6513c570aed325880c032f.gif and /dev/null differ diff --git a/docs/assets/js/00bb4f2e.d6da3795.js b/docs/assets/js/00bb4f2e.d6da3795.js deleted file mode 100644 index 72627dcb..00000000 --- a/docs/assets/js/00bb4f2e.d6da3795.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9563],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=r.createContext({}),l=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),u=l(n),m=i,v=u["".concat(s,".").concat(m)]||u[m]||d[m]||a;return n?r.createElement(v,o(o({ref:t},p),{},{components:n})):r.createElement(v,o({ref:t},p))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=u;var c={};for(var s in t)hasOwnProperty.call(t,s)&&(c[s]=t[s]);c.originalType=e,c.mdxType="string"==typeof e?e:i,o[1]=c;for(var l=2;l{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var r=n(7896),i=(n(2784),n(876));const a={sidebar_position:3},o="LiveView API - `render`",c={unversionedId:"anatomy-of-a-liveview/render-details",id:"anatomy-of-a-liveview/render-details",title:"LiveView API - `render`",description:"render is responsible for taking the context (i.e., state) of the LiveView and generating the HTML/CSS for the",source:"@site/docs/03-anatomy-of-a-liveview/render-details.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/render-details",permalink:"/docs/anatomy-of-a-liveview/render-details",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"LiveView API - `mount`",permalink:"/docs/anatomy-of-a-liveview/mount-details"},next:{title:"LiveView API - `handleEvent`",permalink:"/docs/anatomy-of-a-liveview/handle-event-details"}},s={},l=[{value:"render Signature",id:"render-signature",level:2}],p={toc:l};function d(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveview-api---render"},"LiveView API - ",(0,i.kt)("inlineCode",{parentName:"h1"},"render")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"render")," is responsible for taking the ",(0,i.kt)("inlineCode",{parentName:"p"},"context")," (i.e., state) of the LiveView and generating the HTML/CSS for the\nclient. The ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," framework automatically passes the current ",(0,i.kt)("inlineCode",{parentName:"p"},"context")," of the LiveView into ",(0,i.kt)("inlineCode",{parentName:"p"},"render")," along with\n",(0,i.kt)("inlineCode",{parentName:"p"},"meta")," data (things like the csrfToken, page url, etc.). It uses the ",(0,i.kt)("inlineCode",{parentName:"p"},"html")," method to generate the HTML/CSS for the\nclient."),(0,i.kt)("h2",{id:"render-signature"},(0,i.kt)("inlineCode",{parentName:"h2"},"render")," Signature"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"render(context: TContext, meta: LiveViewMeta): LiveViewTemplate | Promise;\n")),(0,i.kt)("p",null,"The example ",(0,i.kt)("inlineCode",{parentName:"p"},"render")," function below takes the ",(0,i.kt)("inlineCode",{parentName:"p"},"count")," from the context and renders the HTML/CSS for the client:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="counterLiveView.ts" {25-35}',title:'"counterLiveView.ts"',"{25-35}":!0},'import { createLiveView, html } from "liveviewjs";\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const counterLiveView = createLiveView<\n { count: number }, // Define LiveView Context / State\n { type: "increment" } | { type: "decrement" } // Define LiveView Events\n>({\n // Setup / initialize the LiveView Context (i.e., set count to 0)\n mount: (socket) => {\n socket.assign({ count: 0 });\n },\n // Handle incoming increment and decrement events from User input\n handleEvent: (event, socket) => {\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n socket.assign({ count: count + 1 });\n break;\n case "decrement":\n socket.assign({ count: count - 1 });\n break;\n }\n },\n // Renders the Counter View based on the current Context / State\n render: (context) => {\n const { count } = context;\n return html`\n
\n

Count is: ${count}

\n \n \n
\n `;\n },\n});\n')),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"You might have noticed the ",(0,i.kt)("inlineCode",{parentName:"p"},"phx-click")," attributes present in the ",(0,i.kt)("inlineCode",{parentName:"p"},"\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," method would receive an event that looks like this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'{\n type: "increment";\n}\n')),(0,a.kt)("h3",{id:"form-events"},"Form Events"),(0,a.kt)("p",null,"Form events are triggered by the user interacting with form inputs. There are two types of form bindings:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-change")," - When a user changes the value of a form element, the event named by the ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-change")," attribute will be\nsent to the server along with all the form values. This is typically used for form validation purposes prior to form\nsubmission."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-submit")," - This binding is initiated when a user submits a form and the event named by the ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-submit")," attribute\nwill be sent to the server along with all the form values.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'
...
\n')),(0,a.kt)("p",null,"Adding ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-change")," to a form means that any time a user changes the value of a form element, the ",(0,a.kt)("inlineCode",{parentName:"p"},"validate")," event will\nbe sent to the server along with the form values. Typically, ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-change")," handles form validation prior to form\nsubmission."),(0,a.kt)("p",null,"Adding ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-submit")," to a form means that when the user submits the form, the ",(0,a.kt)("inlineCode",{parentName:"p"},"save")," event will be sent to the server\nalong with all the form values."),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"In LiveViewJS, Forms are typically used in conjunction with\n",(0,a.kt)("a",{parentName:"p",href:"/docs/forms-and-changesets/changesets"},(0,a.kt)("inlineCode",{parentName:"a"},"LiveViewChangeset")),"s. ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset"),"s are designed to work together with\nform events to make form validation and submission easy and powerful. We'll dive into more details later on in the next\nsection.")),(0,a.kt)("h3",{id:"key-events"},"Key Events"),(0,a.kt)("p",null,"Key events are triggered by the user pressing a key on the keyboard. There are key bindings for both the element-level\nand the window-level:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-keydown"),", ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-window-keydown")," - When a user presses a key down on the keyboard, the event named by the attribute\nwill be sent to the server along with the key that was pressed."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-keyup"),", ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-window-keyup")," - When a user releases a key on the keyboard, the event named by the attribute will be\nsent to the server along with the key that was released.")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"phx-key")," is an optional attribute which limits triggering of the key events to the key provided in the attribute (e.g.\n",(0,a.kt)("inlineCode",{parentName:"p"},'phx-key="ArrowUp"'),"). You can find a list of the keys on\n",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values"},"MDN Keyboard Values"),"."),(0,a.kt)("h4",{id:"key-event-example"},"Key Event Example"),(0,a.kt)("p",null,"Let's say we want to send the ",(0,a.kt)("inlineCode",{parentName:"p"},"key_event")," event to the server along with the ",(0,a.kt)("inlineCode",{parentName:"p"},'{key: "ArrowUp"}'),' payload when the user\npresses the "ArrowUp" key:'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'
\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," method would receive an event that looks like this:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'{\n type: "key_event",\n key: "ArrowUp"\n}\n')),(0,a.kt)("h3",{id:"focus--blur-events"},"Focus / Blur Events"),(0,a.kt)("p",null,"If a HTML element emits focus and blur events, you can use the following bindings to send events to the server upon\nfocus and/or blur:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-focus")," - When a user focuses on an element, the event named by the ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-focus")," attribute will be sent to the\nserver."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-blur")," - When a user blurs from an element, the event named by the ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-blur")," attribute will be sent to the\nserver.")),(0,a.kt)("p",null,"Similar to key events, there are window-level and element-level bindings:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-window-focus")," - When a user focuses on the window, the event named by the ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-window-focus")," attribute will be\nsent to the server."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-window-blur")," - When a user blurs from the window, the event named by the ",(0,a.kt)("inlineCode",{parentName:"li"},"phx-window-blur")," attribute will be sent\nto the server.")),(0,a.kt)("h4",{id:"focus--blur-examples"},"Focus / Blur Examples"),(0,a.kt)("p",null,"Let's say we want to send the ",(0,a.kt)("inlineCode",{parentName:"p"},"focus_event")," event to the server when the user focuses on the input and the ",(0,a.kt)("inlineCode",{parentName:"p"},"blur_event"),"\nevent when the user blurs from the input"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," method would receive an event that looks like this on focus:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'{\n type: "focus_event";\n}\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," method would receive an event that looks like this on blur:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'{\n type: "blur_event";\n}\n')),(0,a.kt)("h3",{id:"additional-bindings"},"Additional Bindings"),(0,a.kt)("p",null,"There are other bindings that provide additional functionality for your LiveView and work in conjunction with the event\nbindings we reviewed above."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/05a1a0e3.d2b49d64.js b/docs/assets/js/05a1a0e3.d2b49d64.js deleted file mode 100644 index 5e95e181..00000000 --- a/docs/assets/js/05a1a0e3.d2b49d64.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8379],{876:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var i=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=i.createContext({}),s=function(e){var t=i.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},d=function(e){var t=s(e.components);return i.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),m=s(n),h=a,u=m["".concat(p,".").concat(h)]||m[h]||c[h]||o;return n?i.createElement(u,r(r({ref:t},d),{},{components:n})):i.createElement(u,r({ref:t},d))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var i=n(7896),a=(n(2784),n(876));const o={sidebar_position:6},r="LiveViewSocket API - Misc",l={unversionedId:"liveview-socket/liveviewsocket-api-misc",id:"liveview-socket/liveviewsocket-api-misc",title:"LiveViewSocket API - Misc",description:"A few other methods and properties are available on the LiveViewSocket object that we haven't covered yet.",source:"@site/docs/04-liveview-socket/liveviewsocket-api-misc.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/liveviewsocket-api-misc",permalink:"/docs/liveview-socket/liveviewsocket-api-misc",draft:!1,tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket API - Uploads",permalink:"/docs/liveview-socket/liveviewsocket-api-uploads"},next:{title:"Raw `LiveViewSocket` API",permalink:"/docs/liveview-socket/raw-liveviewsocket-api"}},p={},s=[{value:"LiveViewSocket Properties and Methods",id:"liveviewsocket-properties-and-methods",level:2},{value:"id Property",id:"id-property",level:2},{value:"connected Property",id:"connected-property",level:2},{value:"pageTitle Method",id:"pagetitle-method",level:2},{value:"putFlash Method",id:"putflash-method",level:2},{value:"repeat Method",id:"repeat-method",level:2}],d={toc:s};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"liveviewsocket-api---misc"},"LiveViewSocket API - Misc"),(0,a.kt)("p",null,"A few other methods and properties are available on the LiveViewSocket object that we haven't covered yet."),(0,a.kt)("h2",{id:"liveviewsocket-properties-and-methods"},"LiveViewSocket Properties and Methods"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Name"),(0,a.kt)("th",{parentName:"tr",align:null},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"id")," (property, read-only)"),(0,a.kt)("td",{parentName:"tr",align:null},"The (random) id of the LiveView")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"connected")," (property, read-only)"),(0,a.kt)("td",{parentName:"tr",align:null},"Whether the websocket is connected. ",(0,a.kt)("inlineCode",{parentName:"td"},"true")," if connected to a websocket, ",(0,a.kt)("inlineCode",{parentName:"td"},"false")," for http request")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"pageTitle(newPageTitle:string):void;")),(0,a.kt)("td",{parentName:"tr",align:null},"Updates the ",(0,a.kt)("inlineCode",{parentName:"td"},"")," tag of the LiveView. Requires using the ",(0,a.kt)("inlineCode",{parentName:"td"},"live_title")," helper in rendering the page.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"putFlash(key: string, value: string): Promise<void>;")),(0,a.kt)("td",{parentName:"tr",align:null},"Add flash message to the socket for a given key and value.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"repeat(fn: () => void, intervalMillis: number): void;")),(0,a.kt)("td",{parentName:"tr",align:null},"Runs the given function on the given interval until this LiveView is unloaded.")))),(0,a.kt)("h2",{id:"id-property"},(0,a.kt)("inlineCode",{parentName:"h2"},"id")," Property"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"id")," property is a unique identifier for the LiveView. It is a random string that is generated when the LiveView is\ncreated. It is useful for debugging and logging purposes."),(0,a.kt)("h2",{id:"connected-property"},(0,a.kt)("inlineCode",{parentName:"h2"},"connected")," Property"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"connected")," property is a boolean that indicates whether the LiveView is connected to a websocket or not. If the\nLiveView is connected to a websocket, then the value will be ",(0,a.kt)("inlineCode",{parentName:"p"},"true"),". If the LiveView is not connected to a websocket\n(i.e., executing an HTTP request), then the value will be ",(0,a.kt)("inlineCode",{parentName:"p"},"false"),". This is useful for executing logic based on whether\nthe LiveView has completed the initial websocket connection or not. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nif (socket.connected) {\n // only subscribe to the topic if the LiveView is connected to a websocket\n await socket.subscribe("my_topic");\n}\n...\n')),(0,a.kt)("h2",{id:"pagetitle-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"pageTitle")," Method"),(0,a.kt)("p",null,"LiveViewJS provides a ",(0,a.kt)("inlineCode",{parentName:"p"},"live_title_tag")," helper that enables LiveViews to update the ",(0,a.kt)("inlineCode",{parentName:"p"},"<title>")," tag of the page\ndynamically. This is useful for LiveViews that need to update the page title based on the current state of the LiveView.\nFor example, a LiveView may want to update the title to include the details of the item being viewed. The ",(0,a.kt)("inlineCode",{parentName:"p"},"pageTitle"),"\nmethod works in partnership with the ",(0,a.kt)("inlineCode",{parentName:"p"},"live_title_tag")," to enable dynamic page titles. ",(0,a.kt)("inlineCode",{parentName:"p"},"live_title_tag")," is usually used\nin the ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveViewHtmlPageTemplate")," template. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:"{14}","{14}":!0},'export const htmlPageTemplate: LiveViewHtmlPageTemplate = (\n liveTitleOptions: LiveTitleOptions,\n csrfToken: string,\n liveViewContent: LiveViewTemplate\n): LiveViewTemplate => {\n const pageTitle = liveTitleOptions?.title ?? "";\n const pageTitlePrefix = liveTitleOptions?.prefix ?? "";\n const pageTitleSuffix = liveTitleOptions?.suffix ?? "";\n return html`\n <!DOCTYPE html>\n <html lang="en">\n <head>\n <meta name="csrf-token" content="${csrfToken}" />\n ${live_title_tag(pageTitle, { prefix: pageTitlePrefix, suffix: pageTitleSuffix })}\n <script defer type="text/javascript" src="/js/index.js"><\/script>\n </head>\n <body>\n <p><a href="/">\u2190 Back</a><br /><br /></p>\n ${safe(liveViewContent)}\n </body>\n </html>\n `;\n};\n')),(0,a.kt)("p",null,"Now you can update the page title from within the LiveView using the ",(0,a.kt)("inlineCode",{parentName:"p"},"pageTitle")," method. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nsocket.pageTitle("My New Page Title");\n...\n')),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"The ",(0,a.kt)("inlineCode",{parentName:"p"},"pageTitle")," method does not update the ",(0,a.kt)("inlineCode",{parentName:"p"},"prefix")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"suffix")," part of the ",(0,a.kt)("inlineCode",{parentName:"p"},"live_title_tag"),".")),(0,a.kt)("h2",{id:"putflash-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"putFlash")," Method"),(0,a.kt)("p",null,"Flash messages are a way to display small notes or alerts to users to provide feedback on actions they are taken.\n",(0,a.kt)("inlineCode",{parentName:"p"},"putFlash")," is a method that allows you to add a flash message to the LiveView. The flash message will be displayed on\nthe next render. The ",(0,a.kt)("inlineCode",{parentName:"p"},"putFlash")," method takes two arguments: the key and the value. The key is used to identify the flash\nmessage and the value is the message to display. ",(0,a.kt)("inlineCode",{parentName:"p"},"putFlash")," usually works with a ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveViewWrapperTemplate")," to be used as\nthe root template for the LiveView. The ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveViewWrapperTemplate")," is used to display the flash messages. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:"{5-7}","{5-7}":!0},'export const wrapperTemplate: LiveViewWrapperTemplate = async (\n session: SessionData,\n liveViewContent: LiveViewTemplate\n): Promise<LiveViewTemplate> => {\n const flashAdaptor = new SessionFlashAdaptor();\n const infoFlash = (await flashAdaptor.popFlash(session, "info")) || "";\n const errorFlash = (await flashAdaptor.popFlash(session, "error")) || "";\n return html`\n <main role="main" class="container">\n ${infoFlash !== "" ? html`<blockquote><strong>\u2139 Info</strong> ${infoFlash}</blockquote>` : ""} ${errorFlash !== ""\n ? html`<blockquote><strong>\u26a0\ufe0f Error</strong> ${errorFlash}</blockquote>`\n : ""} ${safe(liveViewContent)}\n </main>\n `;\n};\n')),(0,a.kt)("h2",{id:"repeat-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"repeat")," Method"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"repeat")," method is a way to execute a function on a given interval. The ",(0,a.kt)("inlineCode",{parentName:"p"},"repeat")," method takes two arguments: the\nfunction to execute and the interval in milliseconds. The function will be executed on the given interval until the\nLiveView is unloaded. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nif (socket.connected) {\n // only start repeating if the socket is connected\n socket.repeat(() => {\n // send the tick event to `handleInfo` every second\n socket.sendInfo({ type: "tick" });\n }, 1000);\n}\n...\n')))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/05efe52d.09d979ec.js b/docs/assets/js/05efe52d.09d979ec.js deleted file mode 100644 index 64d08219..00000000 --- a/docs/assets/js/05efe52d.09d979ec.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1026],{876:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var a=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function o(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},d=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,d=o(e,["components","mdxType","originalType","parentName"]),m=c(n),h=i,u=m["".concat(l,".").concat(h)]||m[h]||p[h]||r;return n?a.createElement(u,s(s({ref:t},d),{},{components:n})):a.createElement(u,s({ref:t},d))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,s=new Array(r);s[0]=m;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o.mdxType="string"==typeof e?e:i,s[1]=o;for(var c=2;c<r;c++)s[c]=n[c];return a.createElement.apply(null,s)}return a.createElement.apply(null,n)}m.displayName="MDXCreateElement"},329:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>r,metadata:()=>o,toc:()=>c});var a=n(7896),i=(n(2784),n(876));const r={sidebar_position:3},s="Statics and Dynamics",o={unversionedId:"misc/statics-and-dynamics",id:"misc/statics-and-dynamics",title:"Statics and Dynamics",description:"It is helpful to understand why and how LiveViewJS takes a HTML template and breaks it into static and dynamic",source:"@site/docs/13-misc/statics-and-dynamics.md",sourceDirName:"13-misc",slug:"/misc/statics-and-dynamics",permalink:"/docs/misc/statics-and-dynamics",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Live Components",permalink:"/docs/misc/livecomponents"},next:{title:"Debugging LiveViews",permalink:"/docs/misc/debugging-wire"}},l={},c=[{value:"Template Example",id:"template-example",level:2},{value:"Parts of the Template",id:"parts-of-the-template",level:2},{value:"Zip Together",id:"zip-together",level:2},{value:"Send Both Statics and Dynamics to Client",id:"send-both-statics-and-dynamics-to-client",level:2},{value:"Only Update the Dynamics",id:"only-update-the-dynamics",level:2},{value:"Super Fast \ud83c\udfce",id:"super-fast-",level:2}],d={toc:c};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"statics-and-dynamics"},"Statics and Dynamics"),(0,i.kt)("p",null,"It is helpful to understand why and how ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," takes a HTML template and breaks it into static and dynamic\nparts."),(0,i.kt)("h2",{id:"template-example"},"Template Example"),(0,i.kt)("p",null,"Let's say we have the following template being returned in a LiveView's ",(0,i.kt)("inlineCode",{parentName:"p"},"render")," function:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"...\nrender: (context, meta) => {\n const {title, body} = context;\n return html`\n <div>\n <h1>${title}</h1>\n <p>${body}</p>\n </div>\n `\n}\n...\n")),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"html"),' tag is a "tagged template literal" function which allows ',(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," to parse the template literal\ninto a tree of static and dynamic parts. For more information on tagged template literals, see\n",(0,i.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates"},"MDN"),".")),(0,i.kt)("h2",{id:"parts-of-the-template"},"Parts of the Template"),(0,i.kt)("p",null,"The template above is pretty simple but easy to see how it can break into static parts and dynamic parts. There are two\ndynamic parts of the template: ",(0,i.kt)("inlineCode",{parentName:"p"},"${context.title}")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"${context.body}"),". The rest of the template is static. The parts\nbreak down into something like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// array of static parts\nconst statics = [\n "\n <div>\n <h1>",\n "</h1>\n <p>",\n "</p>\n </div>\n "\n];\n\n// array of dynamic parts\nconst dynamics = [\n title,\n body\n];\n\n')),(0,i.kt)("h2",{id:"zip-together"},"Zip Together"),(0,i.kt)("p",null,"You can see that once we resolve the values for ",(0,i.kt)("inlineCode",{parentName:"p"},"title")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"body"),' we can "zip" these two arrays together to create the\nfinal HTML string. This is exactly what ',(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," does when it renders a HTML LiveView."),(0,i.kt)("h2",{id:"send-both-statics-and-dynamics-to-client"},"Send Both Statics and Dynamics to Client"),(0,i.kt)("p",null,"In the case of the websocket, ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," initially sends both the statics and dynamics to the client. The client\nthen uses the statics and dynamics to render the HTML. The client also stores the statics in memory so that it can use\nthem to re-render the HTML when the dynamics change."),(0,i.kt)("h2",{id:"only-update-the-dynamics"},"Only Update the Dynamics"),(0,i.kt)("p",null,"When updates occur on the server and the LiveView is rerendered, we don't need to send the statics again. We only need\nto send the dynamics and furthermore, we only need to send the dynamics that have changed. The client then uses the\nstored statics and the new dynamics to re-render the HTML."),(0,i.kt)("h2",{id:"super-fast-"},"Super Fast \ud83c\udfce"),(0,i.kt)("p",null,"This sending only the value of a dynamic part of the LiveView that changed is extremely efficient and allows\n",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," to be super fast. It also allows ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," to be very lightweight. The client only needs to store\nthe statics in memory and the server only needs to send the dynamics that have changed."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/06e5e56e.5ea46c99.js b/docs/assets/js/06e5e56e.5ea46c99.js deleted file mode 100644 index 34a1929d..00000000 --- a/docs/assets/js/06e5e56e.5ea46c99.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5727],{876:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>u});var a=t(2784);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?i(Object(t),!0).forEach((function(n){r(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):i(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function s(e,n){if(null==e)return{};var t,a,r=function(e,n){if(null==e)return{};var t,a,r={},i=Object.keys(e);for(a=0;a<i.length;a++)t=i[a],n.indexOf(t)>=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)t=i[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),d=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},p=function(e){var n=d(e.components);return a.createElement(l.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},f=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),f=d(t),u=r,m=f["".concat(l,".").concat(u)]||f[u]||c[u]||i;return t?a.createElement(m,o(o({ref:n},p),{},{components:t})):a.createElement(m,o({ref:n},p))}));function u(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var i=t.length,o=new Array(i);o[0]=f;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var d=2;d<i;d++)o[d]=t[d];return a.createElement.apply(null,o)}return a.createElement.apply(null,t)}f.displayName="MDXCreateElement"},8353:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>s,toc:()=>d});var a=t(7896),r=(t(2784),t(876));const i={sidebar_position:6},o="LiveView API - `handleInfo`",s={unversionedId:"anatomy-of-a-liveview/handle-info",id:"anatomy-of-a-liveview/handle-info",title:"LiveView API - `handleInfo`",description:"handleInfo is how server-side events (a.k.a Info) are handled. These server-side events are initiated by processes",source:"@site/docs/03-anatomy-of-a-liveview/handle-info.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/handle-info",permalink:"/docs/anatomy-of-a-liveview/handle-info",draft:!1,tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"LiveView API - `handleParams`",permalink:"/docs/anatomy-of-a-liveview/handle-params"},next:{title:"User-Initiated Event with `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info-user-initiated"}},l={},d=[{value:"<code>handleInfo</code> Signature",id:"handleinfo-signature",level:2},{value:"<code>handleInfo</code> Use Cases",id:"handleinfo-use-cases",level:2}],p={toc:d};function c(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"liveview-api---handleinfo"},"LiveView API - ",(0,r.kt)("inlineCode",{parentName:"h1"},"handleInfo")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo")," is how server-side events (a.k.a ",(0,r.kt)("inlineCode",{parentName:"p"},"Info"),") are handled. These server-side events are initiated by processes\nthat are happening on the server for example: database updates, background jobs, pub/sub messages, or some other\nasynchronous process. Just like ",(0,r.kt)("inlineCode",{parentName:"p"},"handleEvent")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"handleParams"),", ",(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo")," is automatically passed the ",(0,r.kt)("inlineCode",{parentName:"p"},"info")," event\n(i.e., server event) along with the ",(0,r.kt)("inlineCode",{parentName:"p"},"socket")," and can use it to manipulate the ",(0,r.kt)("inlineCode",{parentName:"p"},"context")," of the LiveView or otherwise\nrespond to the ",(0,r.kt)("inlineCode",{parentName:"p"},"info")," messages it receives."),(0,r.kt)("h2",{id:"handleinfo-signature"},(0,r.kt)("inlineCode",{parentName:"h2"},"handleInfo")," Signature"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"handleInfo(info: TInfos, socket: LiveViewSocket<TContext, TInfos>): void | Promise<void>;\n")),(0,r.kt)("h2",{id:"handleinfo-use-cases"},(0,r.kt)("inlineCode",{parentName:"h2"},"handleInfo")," Use Cases"),(0,r.kt)("p",null,"There are three main use cases for ",(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo"),":"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Handling an asynchronous process initiated from a user event without blocking the UI"),(0,r.kt)("li",{parentName:"ul"},"Handling an asynchronous process initiated from a background process"),(0,r.kt)("li",{parentName:"ul"},"Handling a pub/sub message")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/0832cd5e.baac617e.js b/docs/assets/js/0832cd5e.baac617e.js deleted file mode 100644 index ab77ef26..00000000 --- a/docs/assets/js/0832cd5e.baac617e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5742],{876:(t,e,n)=>{n.d(e,{Zo:()=>p,kt:()=>d});var r=n(2784);function o(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function a(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?i(Object(n),!0).forEach((function(e){o(t,e,n[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))}))}return t}function s(t,e){if(null==t)return{};var n,r,o=function(t,e){if(null==t)return{};var n,r,o={},i=Object.keys(t);for(r=0;r<i.length;r++)n=i[r],e.indexOf(n)>=0||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);for(r=0;r<i.length;r++)n=i[r],e.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n])}return o}var c=r.createContext({}),l=function(t){var e=r.useContext(c),n=e;return t&&(n="function"==typeof t?t(e):a(a({},e),t)),n},p=function(t){var e=l(t.components);return r.createElement(c.Provider,{value:e},t.children)},m={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},u=r.forwardRef((function(t,e){var n=t.components,o=t.mdxType,i=t.originalType,c=t.parentName,p=s(t,["components","mdxType","originalType","parentName"]),u=l(n),d=o,f=u["".concat(c,".").concat(d)]||u[d]||m[d]||i;return n?r.createElement(f,a(a({ref:e},p),{},{components:n})):r.createElement(f,a({ref:e},p))}));function d(t,e){var n=arguments,o=e&&e.mdxType;if("string"==typeof t||o){var i=n.length,a=new Array(i);a[0]=u;var s={};for(var c in e)hasOwnProperty.call(e,c)&&(s[c]=e[c]);s.originalType=t,s.mdxType="string"==typeof t?t:o,a[1]=s;for(var l=2;l<i;l++)a[l]=n[l];return r.createElement.apply(null,a)}return r.createElement.apply(null,n)}u.displayName="MDXCreateElement"},9222:(t,e,n)=>{n.r(e),n.d(e,{assets:()=>c,contentTitle:()=>a,default:()=>m,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var r=n(7896),o=(n(2784),n(876));const i={sidebar_position:6},a="Transition Command",s={unversionedId:"js-commands/transition-cmd",id:"js-commands/transition-cmd",title:"Transition Command",description:"The transition command dispatches a DOM event from the target element",source:"@site/docs/11-js-commands/transition-cmd.md",sourceDirName:"11-js-commands",slug:"/js-commands/transition-cmd",permalink:"/docs/js-commands/transition-cmd",draft:!1,tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Dispatch Command",permalink:"/docs/js-commands/dispatch-cmd"},next:{title:"Push Command",permalink:"/docs/js-commands/push-cmd"}},c={},l=[],p={toc:l};function m(t){let{components:e,...n}=t;return(0,o.kt)("wrapper",(0,r.Z)({},p,n,{components:e,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"transition-command"},"Transition Command"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"transition")," command dispatches a DOM event from the target element"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().transition(transition: Transition, options?: TransitionOptions)\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"transition")," - The string of classes to apply to the element, or a 3-tuple containing the transition class, the class\nto apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out duration-300",\n"opacity-100", "opacity-0"]'),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"to")," - An optional css selector to identify the element from which to transition. Defaults to the element that the\nJS Command is attached to."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"time")," - The time (in milliseconds) over which to apply the transition options. Defaults to 200")))),(0,o.kt)("p",null,"Examples"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// transition the target element\n<button\n phx-click="${new JS()\n .transition("fade-in-scale", {\n to: "#transition",\n })\n .show({ to: "#transition", transition: "fade-in-scale" })}">\n Transition In\n</button>\n<button\n phx-click="${new JS()\n .transition("fade-out-scale", {\n to: "#transition",\n })\n .hide({ to: "#transition", transition: "fade-out-scale" })}">\n Transition Out\n</button>\n<div id="transition">Transition Target</div>\n\n// transition button with a shake\n<button phx-click="${new JS().transition("shake")}">Shake</button>\n')))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/096bfee4.9982f285.js b/docs/assets/js/096bfee4.9982f285.js deleted file mode 100644 index 73eb235b..00000000 --- a/docs/assets/js/096bfee4.9982f285.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7178],{5010:e=>{e.exports=JSON.parse('{"permalink":"/blog/tags/facebook","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/0ad92f1a.c6883c73.js b/docs/assets/js/0ad92f1a.c6883c73.js deleted file mode 100644 index 8582be69..00000000 --- a/docs/assets/js/0ad92f1a.c6883c73.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[4221],{876:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>v});var r=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(n),v=i,m=d["".concat(l,".").concat(v)]||d[v]||c[v]||a;return n?r.createElement(m,o(o({ref:t},u),{},{components:n})):r.createElement(m,o({ref:t},u))}));function v(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var p=2;p<a;p++)o[p]=n[p];return r.createElement.apply(null,o)}return r.createElement.apply(null,n)}d.displayName="MDXCreateElement"},6609:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>c,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var r=n(7896),i=(n(2784),n(876));const a={sidebar_position:3},o="NodeJS - Build a LiveView",s={unversionedId:"quick-starts/nodejs-build-first-liveview",id:"quick-starts/nodejs-build-first-liveview",title:"NodeJS - Build a LiveView",description:"Since you've already downloaded the LiveViewJS repo, it should be easy to create a new",source:"@site/docs/02-quick-starts/nodejs-build-first-liveview.md",sourceDirName:"02-quick-starts",slug:"/quick-starts/nodejs-build-first-liveview",permalink:"/docs/quick-starts/nodejs-build-first-liveview",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"NodeJS - Run the Examples",permalink:"/docs/quick-starts/nodejs-run-examples"},next:{title:"Deno - Run the Examples",permalink:"/docs/quick-starts/deno-run-examples"}},l={},p=[{value:"Create a new LiveView in Express",id:"create-a-new-liveview-in-express",level:2},{value:"Setup a new Route",id:"setup-a-new-route",level:2},{value:"Start the Express Server",id:"start-the-express-server",level:2},{value:"See the LiveView in Action",id:"see-the-liveview-in-action",level:2},{value:"Next Steps",id:"next-steps",level:2},{value:"Great start!",id:"great-start",level:2}],u={toc:p};function c(e){let{components:t,...a}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"nodejs---build-a-liveview"},"NodeJS - Build a LiveView"),(0,i.kt)("p",null,"Since you've already ",(0,i.kt)("a",{parentName:"p",href:"get-liveviewjs-repo"},"downloaded the ",(0,i.kt)("strong",{parentName:"a"},"LiveViewJS")," repo"),", it should be easy to create a new\nLiveView and add it to your webserver. Let's get started!"),(0,i.kt)("h2",{id:"create-a-new-liveview-in-express"},"Create a new LiveView in Express"),(0,i.kt)("p",null,"Since we are using Express to serve our LiveViews, we'll create a new LiveView in the ",(0,i.kt)("inlineCode",{parentName:"p"},"packages/express")," directory."),(0,i.kt)("p",null,"Use your favorite editor to create a new file ",(0,i.kt)("inlineCode",{parentName:"p"},"packages/express/src/example/liveview/hello.ts")," and add the following\ncode and hit save:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import { createLiveView, html } from "liveviewjs";\n\nexport const helloLiveView = createLiveView({\n render: () => html`Hello World!`,\n});\n')),(0,i.kt)("p",null,"Congratulations! You've just created your first LiveView! It doesn't do much yet but let's get it running in the\nbrowser."),(0,i.kt)("h2",{id:"setup-a-new-route"},"Setup a new Route"),(0,i.kt)("p",null,"Let's add a route to this LiveView to see it in our browser. Edit ",(0,i.kt)("inlineCode",{parentName:"p"},"packages/express/src/example/index.ts")," and make the\nfollowing highlighted changes:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="packages/express/src/example/index.ts" {3,7}',title:'"packages/express/src/example/index.ts"',"{3,7}":!0},'...\nimport { htmlPageTemplate, wrapperTemplate } from "./liveViewRenderers";\nimport { helloLiveView } from "./liveview/hello";\n\n// map request paths to LiveViews\nconst router: LiveViewRouter = {\n "/hello": helloLiveView,\n "/autocomplete": autocompleteLiveView,\n...\n')),(0,i.kt)("p",null,"Great! We've now setup our new LiveView to be served at the ",(0,i.kt)("inlineCode",{parentName:"p"},"/hello")," path. Let's start the server and see it in action."),(0,i.kt)("h2",{id:"start-the-express-server"},"Start the Express Server"),(0,i.kt)("p",null,"First, load the NPM dependencies:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# install the NPM dependencies\nnpm install\n")),(0,i.kt)("p",null,"Then, start the express server:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# start express server\nnpm run start -w packages/express\n")),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"You will probably see a warning from NodeJS about using an experimental feature:"),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre"},"ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time\n(Use `node --trace-warnings ...` to show where the warning was created)\n")),(0,i.kt)("p",{parentName:"admonition"},"The feature we are using is the built-in ",(0,i.kt)("inlineCode",{parentName:"p"},"fetch")," method. Feel free to ignore this warning.")),(0,i.kt)("h2",{id:"see-the-liveview-in-action"},"See the LiveView in Action"),(0,i.kt)("p",null,"Point your browser to ",(0,i.kt)("a",{parentName:"p",href:"http://localhost:4001/hello"},"http://localhost:4001/hello"),", and you should see something like the\nfollowing: ",(0,i.kt)("img",{alt:"LiveViewJS Hello World Screenshot",src:n(1906).Z,width:"1630",height:"632"})),(0,i.kt)("h2",{id:"next-steps"},"Next Steps"),(0,i.kt)("p",null,"Ok, we got our first LiveView running but it isn't very interactive. Let's make it more interesting by adding a button\nthat toggles between using text and emojis to say hello. Update the ",(0,i.kt)("inlineCode",{parentName:"p"},"hello.ts")," file to the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="packages/express/src/example/liveview/hello.ts"',title:'"packages/express/src/example/liveview/hello.ts"'},'import { createLiveView, html } from "liveviewjs";\n\nexport const helloLiveView = createLiveView({\n mount: (socket) => {\n socket.assign({ useEmoji: false });\n },\n handleEvent(event, socket) {\n socket.assign({ useEmoji: !socket.context.useEmoji });\n },\n render: (context) => {\n const msg = context.useEmoji ? "\ud83d\udc4b \ud83c\udf0e" : "Hello World";\n return html`\n ${msg}\n <br />\n <button phx-click="toggle">Toggle Message</button>\n `;\n },\n});\n')),(0,i.kt)("p",null,"Now, when you refresh the page, you should see a button that toggles between using text and emojis to say hello. It\nshould look something like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"LiveViewJS Hello World Recording",src:n(7377).Z,width:"1630",height:"630"})),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"You'll notice that ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," automatically rebuilds and reloads the server when you make changes to your\nLiveView code. This is a great way to iterate quickly on your LiveView.")),(0,i.kt)("h2",{id:"great-start"},"Great start!"),(0,i.kt)("p",null,"You've just created your first LiveView and added it to your webserver! There is a lot more to learn about\n",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," but you are well on your way. We recommend you continue to the\n",(0,i.kt)("a",{parentName:"p",href:"/docs/category/anatomy-of-a-liveview"},"Anatomy of a LiveView")," section to start to learn more about how LiveViews work."))}c.isMDXComponent=!0},1906:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/liveviewjs_hello_liveview-09cb9e9d8588fb31e92cbefd4d4d6ddf.png"},7377:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/liveviewjs_hello_toggle_liveview_rec-dff5867dec6513c570aed325880c032f.gif"}}]); \ No newline at end of file diff --git a/docs/assets/js/0bd32f56.9cbc10ae.js b/docs/assets/js/0bd32f56.9cbc10ae.js deleted file mode 100644 index d520fc95..00000000 --- a/docs/assets/js/0bd32f56.9cbc10ae.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8030],{876:(e,r,t)=>{t.d(r,{Zo:()=>p,kt:()=>m});var n=t(2784);function o(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function a(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function i(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?a(Object(t),!0).forEach((function(r){o(e,r,t[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):a(Object(t)).forEach((function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}))}return e}function s(e,r){if(null==e)return{};var t,n,o=function(e,r){if(null==e)return{};var t,n,o={},a=Object.keys(e);for(n=0;n<a.length;n++)t=a[n],r.indexOf(t)>=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n<a.length;n++)t=a[n],r.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var c=n.createContext({}),l=function(e){var r=n.useContext(c),t=r;return e&&(t="function"==typeof e?e(r):i(i({},r),e)),t},p=function(e){var r=l(e.components);return n.createElement(c.Provider,{value:r},e.children)},u={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},f=n.forwardRef((function(e,r){var t=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),f=l(t),m=o,d=f["".concat(c,".").concat(m)]||f[m]||u[m]||a;return t?n.createElement(d,i(i({ref:r},p),{},{components:t})):n.createElement(d,i({ref:r},p))}));function m(e,r){var t=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=f;var s={};for(var c in r)hasOwnProperty.call(r,c)&&(s[c]=r[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var l=2;l<a;l++)i[l]=t[l];return n.createElement.apply(null,i)}return n.createElement.apply(null,t)}f.displayName="MDXCreateElement"},3180:(e,r,t)=>{t.r(r),t.d(r,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var n=t(7896),o=(t(2784),t(876));const a={sidebar_position:6},i="Flash Messages",s={unversionedId:"misc/flash",id:"misc/flash",title:"Flash Messages",description:"",source:"@site/docs/13-misc/flash.md",sourceDirName:"13-misc",slug:"/misc/flash",permalink:"/docs/misc/flash",draft:!1,tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Root and Page Renderers",permalink:"/docs/misc/root-and-page-renderers"}},c={},l=[],p={toc:l};function u(e){let{components:r,...t}=e;return(0,o.kt)("wrapper",(0,n.Z)({},p,t,{components:r,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"flash-messages"},"Flash Messages"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/0e8a1ffd.3bb752e2.js b/docs/assets/js/0e8a1ffd.3bb752e2.js deleted file mode 100644 index 53023d3d..00000000 --- a/docs/assets/js/0e8a1ffd.3bb752e2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3304],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var o=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,a=function(e,t){if(null==e)return{};var n,o,a={},i=Object.keys(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},v=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),v=c(n),h=a,u=v["".concat(l,".").concat(h)]||v[h]||d[h]||i;return n?o.createElement(u,r(r({ref:t},p),{},{components:n})):o.createElement(u,r({ref:t},p))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,r=new Array(i);r[0]=v;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,r[1]=s;for(var c=2;c<i;c++)r[c]=n[c];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}v.displayName="MDXCreateElement"},2667:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>c});var o=n(7896),a=(n(2784),n(876));const i={sidebar_position:2},r="LiveViewSocket API - Context",s={unversionedId:"liveview-socket/liveviewsocket-api-context",id:"liveview-socket/liveviewsocket-api-context",title:"LiveViewSocket API - Context",description:'The "context" of a LiveView is the current state of the LiveView. One way to think of a LiveView is as a set of methods',source:"@site/docs/04-liveview-socket/liveviewsocket-api-context.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/liveviewsocket-api-context",permalink:"/docs/liveview-socket/liveviewsocket-api-context",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket API",permalink:"/docs/liveview-socket/liveviewsocket-api"},next:{title:"LiveViewSocket API - Push",permalink:"/docs/liveview-socket/liveviewsocket-api-push"}},l={},c=[{value:"Context Properties and Methods on the <code>LiveViewSocket</code>",id:"context-properties-and-methods-on-the-liveviewsocket",level:2},{value:"Details",id:"details",level:2},{value:"Context Type Annotations",id:"context-type-annotations",level:2},{value:"Context Persisted for the Life of the LiveView",id:"context-persisted-for-the-life-of-the-liveview",level:2},{value:"Temporary Data for Context",id:"temporary-data-for-context",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,o.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"liveviewsocket-api---context"},"LiveViewSocket API - Context"),(0,a.kt)("p",null,'The "context" of a LiveView is the current state of the LiveView. One way to think of a LiveView is as a set of methods\nthat handle events, read and write the context, and render a view based on the data in the context. Obviously,\nproperties and methods that manipulate the context are very important to a LiveView.'),(0,a.kt)("h2",{id:"context-properties-and-methods-on-the-liveviewsocket"},"Context Properties and Methods on the ",(0,a.kt)("inlineCode",{parentName:"h2"},"LiveViewSocket")),(0,a.kt)("p",null,"Three parts of the ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," are used to manipulate the context:"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Name"),(0,a.kt)("th",{parentName:"tr",align:null},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"context")," (property, read-only)"),(0,a.kt)("td",{parentName:"tr",align:null},"The current context (i.e., state) of the LiveView")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"assign(context:Partial<TContext>):void;")),(0,a.kt)("td",{parentName:"tr",align:null},"Update the context (i.e., state) of the LiveView")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"tempAssign(context:Partial<TContext>):void;")),(0,a.kt)("td",{parentName:"tr",align:null},"Marks any set properties as temporary and will be reset to the given value after the next render cycle. Typically used to ensure large but infrequently updated values are not kept in memory.")))),(0,a.kt)("h2",{id:"details"},"Details"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"socket.assign")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"socket.context")," are the work-horse methods for manipulating and reading the state of the LiveView.\nThe ",(0,a.kt)("inlineCode",{parentName:"p"},"assign")," method is used to update the state of the LiveView and the ",(0,a.kt)("inlineCode",{parentName:"p"},"context")," property is used to read the state of\nthe LiveView."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// Update the context (i.e., current state) of the `LiveView`\nsocket.assign({ foo: "bar" });\n')),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// Read the context (i.e., current state) of the `LiveView`\nif (socket.context.foo === "baz") {\n // do something\n}\n// or destructure data from the context\nconst { foo } = socket.context;\n')),(0,a.kt)("h2",{id:"context-type-annotations"},"Context Type Annotations"),(0,a.kt)("p",null,"When creating a ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveView"),", developers can provide a type annotation for ",(0,a.kt)("inlineCode",{parentName:"p"},"TContext"),' which describes the "shape" of the\ncontext for that LiveView. This is useful for providing type safety and autocomplete for the context (in Typescript).'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'\n// You can define the "shape" of the TContext by annotating the createLiveView function\nconst myLiveView = createLiveView<{foo: string}>(\n mount: (socket) => {\n socket.assign({ foo: "bar" });\n ...\n socket.assign({ baz: "qux" }); // type error no "baz" property in context\n }\n ...\n)\n')),(0,a.kt)("p",null,"You can type the Context inline as above or you can define the context type first and then use it as a type\nannotation:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// Define the MyContext interface\ninterface MyContext { foo: string };\n// Annotate the createLiveView function with the MyContext interface\nconst myLiveView = createLiveView<MyContext>(...)\n")),(0,a.kt)("h2",{id:"context-persisted-for-the-life-of-the-liveview"},"Context Persisted for the Life of the LiveView"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"context")," of a LiveView is persisted on the server (in memory by default) which means any data added to the\n",(0,a.kt)("inlineCode",{parentName:"p"},"context")," (via ",(0,a.kt)("inlineCode",{parentName:"p"},"assign"),") will be stored until that LiveView instance is cleaned up."),(0,a.kt)("h2",{id:"temporary-data-for-context"},"Temporary Data for Context"),(0,a.kt)("p",null,"Sometimes you want to add data to a ",(0,a.kt)("inlineCode",{parentName:"p"},"context")," that is temporary ","\u2014",' that is, only added to the context for one\n"render cycle". There is a method called ',(0,a.kt)("inlineCode",{parentName:"p"},"socket.tempAssign")," that allows a developer to tell ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," to set a\n",(0,a.kt)("inlineCode",{parentName:"p"},"context")," property to a given value after the render cycle. Typically this is used for large objects or collections that\ndon't change often and therefore probably don't need to be stored in memory (e.g., collection of users or messages,\netc)."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// first assign a large object to the context\nsocket.assign({ photos: [\n ...// 10s, 100s, 1000s, of photos\n]});\n// use tempAssign to tell LiveViewJS to clear the photos array after this render cycle\nsocket.tempAssign({ photos: [] });\n")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/11e44ae3.c955dcae.js b/docs/assets/js/11e44ae3.c955dcae.js deleted file mode 100644 index e99c72fe..00000000 --- a/docs/assets/js/11e44ae3.c955dcae.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6739],{876:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>f});var n=r(2784);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){o(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),u=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},s=function(e){var t=u(e.components);return n.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),d=u(r),f=o,m=d["".concat(l,".").concat(f)]||d[f]||p[f]||i;return r?n.createElement(m,a(a({ref:t},s),{},{components:r})):n.createElement(m,a({ref:t},s))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var u=2;u<i;u++)a[u]=r[u];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}d.displayName="MDXCreateElement"},9456:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>p,frontMatter:()=>i,metadata:()=>c,toc:()=>u});var n=r(7896),o=(r(2784),r(876));const i={sidebar_position:5},a="Gratitude",c={unversionedId:"overview/gratitude",id:"overview/gratitude",title:"Gratitude",description:"\ud83d\ude4c Thanks to the Phoenix LiveView team for the genius conceptualization and implementation of LiveViews and all the code",source:"@site/docs/01-overview/gratitude.md",sourceDirName:"01-overview",slug:"/overview/gratitude",permalink:"/docs/overview/gratitude",draft:!1,tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"Getting Involved",permalink:"/docs/overview/feedback"},next:{title:"Quick Starts",permalink:"/docs/category/quick-starts"}},l={},u=[],s={toc:u};function p(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"gratitude"},"Gratitude"),(0,o.kt)("p",null,"\ud83d\ude4c Thanks to the Phoenix LiveView team for the genius conceptualization and implementation of LiveViews and all the code\nwe can reuse on the client side."),(0,o.kt)("p",null,"\ud83d\ude4c Thanks to ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/ogrodnek"},"@ogrodnek")," for the early support, feedback, and the idea to reuse the Phoenix\nclient code instead of reinventing!"),(0,o.kt)("p",null,"\ud83d\ude4c Thanks to ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/blimmer/"},"@blimmer")," for the awesome feedback, documentation suggestions, and support!"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/12d121b1.9e468a56.js b/docs/assets/js/12d121b1.9e468a56.js deleted file mode 100644 index 23fc2aee..00000000 --- a/docs/assets/js/12d121b1.9e468a56.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9133],{876:(e,r,t)=>{t.d(r,{Zo:()=>l,kt:()=>g});var n=t(2784);function i(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function o(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function a(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?o(Object(t),!0).forEach((function(r){i(e,r,t[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):o(Object(t)).forEach((function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}))}return e}function c(e,r){if(null==e)return{};var t,n,i=function(e,r){if(null==e)return{};var t,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)t=o[n],r.indexOf(t)>=0||(i[t]=e[t]);return i}(e,r);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)t=o[n],r.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=n.createContext({}),u=function(e){var r=n.useContext(s),t=r;return e&&(t="function"==typeof e?e(r):a(a({},r),e)),t},l=function(e){var r=u(e.components);return n.createElement(s.Provider,{value:r},e.children)},p={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},d=n.forwardRef((function(e,r){var t=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),d=u(t),g=i,m=d["".concat(s,".").concat(g)]||d[g]||p[g]||o;return t?n.createElement(m,a(a({ref:r},l),{},{components:t})):n.createElement(m,a({ref:r},l))}));function g(e,r){var t=arguments,i=r&&r.mdxType;if("string"==typeof e||i){var o=t.length,a=new Array(o);a[0]=d;var c={};for(var s in r)hasOwnProperty.call(r,s)&&(c[s]=r[s]);c.originalType=e,c.mdxType="string"==typeof e?e:i,a[1]=c;for(var u=2;u<o;u++)a[u]=t[u];return n.createElement.apply(null,a)}return n.createElement.apply(null,t)}d.displayName="MDXCreateElement"},1853:(e,r,t)=>{t.r(r),t.d(r,{assets:()=>s,contentTitle:()=>a,default:()=>p,frontMatter:()=>o,metadata:()=>c,toc:()=>u});var n=t(7896),i=(t(2784),t(876));const o={sidebar_position:4},a="Debugging LiveViews",c={unversionedId:"misc/debugging-wire",id:"misc/debugging-wire",title:"Debugging LiveViews",description:"",source:"@site/docs/13-misc/debugging-wire.md",sourceDirName:"13-misc",slug:"/misc/debugging-wire",permalink:"/docs/misc/debugging-wire",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Statics and Dynamics",permalink:"/docs/misc/statics-and-dynamics"},next:{title:"Root and Page Renderers",permalink:"/docs/misc/root-and-page-renderers"}},s={},u=[],l={toc:u};function p(e){let{components:r,...t}=e;return(0,i.kt)("wrapper",(0,n.Z)({},l,t,{components:r,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"debugging-liveviews"},"Debugging LiveViews"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/14eb3368.1ae9325c.js b/docs/assets/js/14eb3368.1ae9325c.js deleted file mode 100644 index dc842103..00000000 --- a/docs/assets/js/14eb3368.1ae9325c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9817],{7186:(e,t,n)=>{n.d(t,{Z:()=>p});var a=n(7896),r=n(2784),i=n(6277),l=n(211),s=n(4855),c=n(7661),o=n(9817),m=n(77),d=n(1077);function u(e){return r.createElement("svg",(0,a.Z)({viewBox:"0 0 24 24"},e),r.createElement("path",{d:"M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z",fill:"currentColor"}))}const h={breadcrumbsContainer:"breadcrumbsContainer_zCmv",breadcrumbHomeIcon:"breadcrumbHomeIcon_tMMf"};function b(e){let{children:t,href:n,isLast:a}=e;const i="breadcrumbs__link";return a?r.createElement("span",{className:i,itemProp:"name"},t):n?r.createElement(o.Z,{className:i,href:n,itemProp:"item"},r.createElement("span",{itemProp:"name"},t)):r.createElement("span",{className:i},t)}function v(e){let{children:t,active:n,index:l,addMicrodata:s}=e;return r.createElement("li",(0,a.Z)({},s&&{itemScope:!0,itemProp:"itemListElement",itemType:"https://schema.org/ListItem"},{className:(0,i.Z)("breadcrumbs__item",{"breadcrumbs__item--active":n})}),t,r.createElement("meta",{itemProp:"position",content:String(l+1)}))}function g(){const e=(0,m.Z)("/");return r.createElement("li",{className:"breadcrumbs__item"},r.createElement(o.Z,{"aria-label":(0,d.I)({id:"theme.docs.breadcrumbs.home",message:"Home page",description:"The ARIA label for the home page in the breadcrumbs"}),className:(0,i.Z)("breadcrumbs__link",h.breadcrumbsItemLink),href:e},r.createElement(u,{className:h.breadcrumbHomeIcon})))}function p(){const e=(0,s.s1)(),t=(0,c.Ns)();return e?r.createElement("nav",{className:(0,i.Z)(l.k.docs.docBreadcrumbs,h.breadcrumbsContainer),"aria-label":(0,d.I)({id:"theme.docs.breadcrumbs.navAriaLabel",message:"Breadcrumbs",description:"The ARIA label for the breadcrumbs"})},r.createElement("ul",{className:"breadcrumbs",itemScope:!0,itemType:"https://schema.org/BreadcrumbList"},t&&r.createElement(g,null),e.map(((t,n)=>{const a=n===e.length-1;return r.createElement(v,{key:n,active:a,index:n,addMicrodata:!!t.href},r.createElement(b,{href:t.href,isLast:a},t.label))})))):null}},6544:(e,t,n)=>{n.r(t),n.d(t,{default:()=>C});var a=n(2784),r=n(328),i=n(4855),l=n(77),s=n(6277),c=n(9817),o=n(1344),m=n(1077);const d="cardContainer_Shn5",u="cardTitle_h48N",h="cardDescription_CytT";function b(e){let{href:t,children:n}=e;return a.createElement(c.Z,{href:t,className:(0,s.Z)("card padding--lg",d)},n)}function v(e){let{href:t,icon:n,title:r,description:i}=e;return a.createElement(b,{href:t},a.createElement("h2",{className:(0,s.Z)("text--truncate",u),title:r},n," ",r),i&&a.createElement("p",{className:(0,s.Z)("text--truncate",h),title:i},i))}function g(e){let{item:t}=e;const n=(0,i.Wl)(t);return n?a.createElement(v,{href:n,icon:"\ud83d\uddc3\ufe0f",title:t.label,description:(0,m.I)({message:"{count} items",id:"theme.docs.DocCard.categoryDescription",description:"The default description for a category card in the generated index about how many items this category includes"},{count:t.items.length})}):null}function p(e){var t;let{item:n}=e;const r=(0,o.Z)(n.href)?"\ud83d\udcc4\ufe0f":"\ud83d\udd17",l=(0,i.xz)(null!=(t=n.docId)?t:void 0);return a.createElement(v,{href:n.href,icon:r,title:n.label,description:null==l?void 0:l.description})}function E(e){let{item:t}=e;switch(t.type){case"link":return a.createElement(p,{item:t});case"category":return a.createElement(g,{item:t});default:throw new Error("unknown item type "+JSON.stringify(t))}}function f(e){let{items:t,className:n}=e;return a.createElement("section",{className:(0,s.Z)("row",n)},function(e){return e.filter((e=>"category"!==e.type||!!(0,i.Wl)(e)))}(t).map(((e,t)=>a.createElement("article",{key:t,className:"col col--6 margin-bottom--lg"},a.createElement(E,{item:e})))))}var Z=n(2992),N=n(2269),k=n(8541),_=n(7186),L=n(3851);const T="generatedIndexPage_ak01",y="list_iQEt",x="title_AulG";function I(e){let{categoryGeneratedIndex:t}=e;return a.createElement(r.d,{title:t.title,description:t.description,keywords:t.keywords,image:(0,l.Z)(t.image)})}function w(e){let{categoryGeneratedIndex:t}=e;const n=(0,i.jA)();return a.createElement("div",{className:T},a.createElement(N.Z,null),a.createElement(_.Z,null),a.createElement(k.Z,null),a.createElement("header",null,a.createElement(L.Z,{as:"h1",className:x},t.title),t.description&&a.createElement("p",null,t.description)),a.createElement("article",{className:"margin-top--lg"},a.createElement(f,{items:n.items,className:y})),a.createElement("footer",{className:"margin-top--lg"},a.createElement(Z.Z,{previous:t.navigation.previous,next:t.navigation.next})))}function C(e){return a.createElement(a.Fragment,null,a.createElement(I,e),a.createElement(w,e))}},2992:(e,t,n)=>{n.d(t,{Z:()=>s});var a=n(7896),r=n(2784),i=n(1077),l=n(7066);function s(e){const{previous:t,next:n}=e;return r.createElement("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,i.I)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages navigation",description:"The ARIA label for the docs pagination"})},t&&r.createElement(l.Z,(0,a.Z)({},t,{subLabel:r.createElement(i.Z,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc"},"Previous")})),n&&r.createElement(l.Z,(0,a.Z)({},n,{subLabel:r.createElement(i.Z,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc"},"Next"),isNext:!0})))}},8541:(e,t,n)=>{n.d(t,{Z:()=>c});var a=n(2784),r=n(6277),i=n(1077),l=n(211),s=n(5663);function c(e){let{className:t}=e;const n=(0,s.E)();return n.badge?a.createElement("span",{className:(0,r.Z)(t,l.k.docs.docVersionBadge,"badge badge--secondary")},a.createElement(i.Z,{id:"theme.docs.versionBadge.label",values:{versionLabel:n.label}},"Version: {versionLabel}")):null}},2269:(e,t,n)=>{n.d(t,{Z:()=>g});var a=n(2784),r=n(6277),i=n(7614),l=n(9817),s=n(1077),c=n(1215),o=n(211),m=n(7949),d=n(5663);const u={unreleased:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(s.Z,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is unreleased documentation for {siteTitle} {versionLabel} version.")},unmaintained:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(s.Z,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.")}};function h(e){const t=u[e.versionMetadata.banner];return a.createElement(t,e)}function b(e){let{versionLabel:t,to:n,onClick:r}=e;return a.createElement(s.Z,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:t,latestVersionLink:a.createElement("b",null,a.createElement(l.Z,{to:n,onClick:r},a.createElement(s.Z,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label"},"latest version")))}},"For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).")}function v(e){let{className:t,versionMetadata:n}=e;const{siteConfig:{title:l}}=(0,i.Z)(),{pluginId:s}=(0,c.gA)({failfast:!0}),{savePreferredVersionName:d}=(0,m.J)(s),{latestDocSuggestion:u,latestVersionSuggestion:v}=(0,c.Jo)(s),g=null!=u?u:(p=v).docs.find((e=>e.id===p.mainDocId));var p;return a.createElement("div",{className:(0,r.Z)(t,o.k.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert"},a.createElement("div",null,a.createElement(h,{siteTitle:l,versionMetadata:n})),a.createElement("div",{className:"margin-top--md"},a.createElement(b,{versionLabel:v.label,to:g.path,onClick:()=>d(v.name)})))}function g(e){let{className:t}=e;const n=(0,d.E)();return n.banner?a.createElement(v,{className:t,versionMetadata:n}):null}},3851:(e,t,n)=>{n.d(t,{Z:()=>m});var a=n(7896),r=n(2784),i=n(6277),l=n(1077),s=n(7683);const c="anchorWithStickyNavbar_fF9Z",o="anchorWithHideOnScrollNavbar_Yh18";function m(e){let{as:t,id:n,...m}=e;const{navbar:{hideOnScroll:d}}=(0,s.L)();return"h1"!==t&&n?r.createElement(t,(0,a.Z)({},m,{className:(0,i.Z)("anchor",d?o:c),id:n}),m.children,r.createElement("a",{className:"hash-link",href:"#"+n,title:(0,l.I)({id:"theme.common.headingLinkTitle",message:"Direct link to heading",description:"Title for link to heading"})},"\u200b")):r.createElement(t,(0,a.Z)({},m,{id:void 0}))}},7066:(e,t,n)=>{n.d(t,{Z:()=>l});var a=n(2784),r=n(6277),i=n(9817);function l(e){const{permalink:t,title:n,subLabel:l,isNext:s}=e;return a.createElement(i.Z,{className:(0,r.Z)("pagination-nav__link",s?"pagination-nav__link--next":"pagination-nav__link--prev"),to:t},l&&a.createElement("div",{className:"pagination-nav__sublabel"},l),a.createElement("div",{className:"pagination-nav__label"},n))}}}]); \ No newline at end of file diff --git a/docs/assets/js/15989f81.aaa42acf.js b/docs/assets/js/15989f81.aaa42acf.js deleted file mode 100644 index e68770f5..00000000 --- a/docs/assets/js/15989f81.aaa42acf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[610],{876:(e,n,t)=>{t.d(n,{Zo:()=>d,kt:()=>v});var r=t(2784);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?i(Object(t),!0).forEach((function(n){a(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):i(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function c(e,n){if(null==e)return{};var t,r,a=function(e,n){if(null==e)return{};var t,r,a={},i=Object.keys(e);for(r=0;r<i.length;r++)t=i[r],n.indexOf(t)>=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)t=i[r],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var s=r.createContext({}),l=function(e){var n=r.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},d=function(e){var n=l(e.components);return r.createElement(s.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},u=r.forwardRef((function(e,n){var t=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,d=c(e,["components","mdxType","originalType","parentName"]),u=l(t),v=a,m=u["".concat(s,".").concat(v)]||u[v]||p[v]||i;return t?r.createElement(m,o(o({ref:n},d),{},{components:t})):r.createElement(m,o({ref:n},d))}));function v(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var i=t.length,o=new Array(i);o[0]=u;var c={};for(var s in n)hasOwnProperty.call(n,s)&&(c[s]=n[s]);c.originalType=e,c.mdxType="string"==typeof e?e:a,o[1]=c;for(var l=2;l<i;l++)o[l]=t[l];return r.createElement.apply(null,o)}return r.createElement.apply(null,t)}u.displayName="MDXCreateElement"},7522:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>i,metadata:()=>c,toc:()=>l});var r=t(7896),a=(t(2784),t(876));const i={sidebar_position:4},o="LiveView API - `handleEvent`",c={unversionedId:"anatomy-of-a-liveview/handle-event-details",id:"anatomy-of-a-liveview/handle-event-details",title:"LiveView API - `handleEvent`",description:"handleEvent is called automatically by the LiveViewJS framework when a user action causes the browser to send",source:"@site/docs/03-anatomy-of-a-liveview/handle-event-details.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/handle-event-details",permalink:"/docs/anatomy-of-a-liveview/handle-event-details",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"LiveView API - `render`",permalink:"/docs/anatomy-of-a-liveview/render-details"},next:{title:"LiveView API - `handleParams`",permalink:"/docs/anatomy-of-a-liveview/handle-params"}},s={},l=[{value:"<code>handleEvent</code> Signature",id:"handleevent-signature",level:2}],d={toc:l};function p(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,r.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"liveview-api---handleevent"},"LiveView API - ",(0,a.kt)("inlineCode",{parentName:"h1"},"handleEvent")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," is called automatically by the ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," framework when a user action causes the browser to send\nan event to the server (e.g., clicks, keyboard input, form updates, focus/blur, etc.). (More details on\n",(0,a.kt)("a",{parentName:"p",href:"/docs/user-events-slash-bindings/overview"},"User Events"),"). The ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," function is responsible for updating the\n",(0,a.kt)("inlineCode",{parentName:"p"},"context")," (i.e., state) of the LiveView based on the event."),(0,a.kt)("h2",{id:"handleevent-signature"},(0,a.kt)("inlineCode",{parentName:"h2"},"handleEvent")," Signature"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"handleEvent(event: TEvents, socket: LiveViewSocket<TContext, TInfos>): void | Promise<void>;\n")),(0,a.kt)("p",null,"The example ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," function below receives the ",(0,a.kt)("inlineCode",{parentName:"p"},"event")," and the ",(0,a.kt)("inlineCode",{parentName:"p"},"socket")," and updates the ",(0,a.kt)("inlineCode",{parentName:"p"},"count")," in the ",(0,a.kt)("inlineCode",{parentName:"p"},"socket"),"'s\ncontext based on the ",(0,a.kt)("inlineCode",{parentName:"p"},"event"),"'s ",(0,a.kt)("inlineCode",{parentName:"p"},"type"),". In other words, it adds 1 when to ",(0,a.kt)("inlineCode",{parentName:"p"},"count")," when it receives the ",(0,a.kt)("inlineCode",{parentName:"p"},"increment")," event\nand subtracts 1 when it receives the ",(0,a.kt)("inlineCode",{parentName:"p"},"decrement")," event:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="counterLiveView.ts" {13-24}',title:'"counterLiveView.ts"',"{13-24}":!0},'import { createLiveView, html } from "liveviewjs";\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const counterLiveView = createLiveView<\n { count: number }, // Define LiveView Context / State\n { type: "increment" } | { type: "decrement" } // Define LiveView Events\n>({\n // Setup / initialize the LiveView Context (i.e., set count to 0)\n mount: (socket) => {\n socket.assign({ count: 0 });\n },\n // Handle incoming increment and decrement events from User input\n handleEvent: (event, socket) => {\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n socket.assign({ count: count + 1 });\n break;\n case "decrement":\n socket.assign({ count: count - 1 });\n break;\n }\n },\n // Renders the Counter View based on the current Context / State\n render: (context) => {\n const { count } = context;\n return html`\n <div>\n <h1>Count is: ${count}</h1>\n <button phx-click="decrement">-</button>\n <button phx-click="increment">+</button>\n </div>\n `;\n },\n});\n')))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/17896441.0c65f063.js b/docs/assets/js/17896441.0c65f063.js deleted file mode 100644 index 312c0177..00000000 --- a/docs/assets/js/17896441.0c65f063.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7918],{7186:(e,t,n)=>{n.d(t,{Z:()=>f});var a=n(7896),l=n(2784),r=n(6277),s=n(211),o=n(4855),c=n(7661),i=n(9817),d=n(77),m=n(1077);function u(e){return l.createElement("svg",(0,a.Z)({viewBox:"0 0 24 24"},e),l.createElement("path",{d:"M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z",fill:"currentColor"}))}const v={breadcrumbsContainer:"breadcrumbsContainer_zCmv",breadcrumbHomeIcon:"breadcrumbHomeIcon_tMMf"};function b(e){let{children:t,href:n,isLast:a}=e;const r="breadcrumbs__link";return a?l.createElement("span",{className:r,itemProp:"name"},t):n?l.createElement(i.Z,{className:r,href:n,itemProp:"item"},l.createElement("span",{itemProp:"name"},t)):l.createElement("span",{className:r},t)}function h(e){let{children:t,active:n,index:s,addMicrodata:o}=e;return l.createElement("li",(0,a.Z)({},o&&{itemScope:!0,itemProp:"itemListElement",itemType:"https://schema.org/ListItem"},{className:(0,r.Z)("breadcrumbs__item",{"breadcrumbs__item--active":n})}),t,l.createElement("meta",{itemProp:"position",content:String(s+1)}))}function p(){const e=(0,d.Z)("/");return l.createElement("li",{className:"breadcrumbs__item"},l.createElement(i.Z,{"aria-label":(0,m.I)({id:"theme.docs.breadcrumbs.home",message:"Home page",description:"The ARIA label for the home page in the breadcrumbs"}),className:(0,r.Z)("breadcrumbs__link",v.breadcrumbsItemLink),href:e},l.createElement(u,{className:v.breadcrumbHomeIcon})))}function f(){const e=(0,o.s1)(),t=(0,c.Ns)();return e?l.createElement("nav",{className:(0,r.Z)(s.k.docs.docBreadcrumbs,v.breadcrumbsContainer),"aria-label":(0,m.I)({id:"theme.docs.breadcrumbs.navAriaLabel",message:"Breadcrumbs",description:"The ARIA label for the breadcrumbs"})},l.createElement("ul",{className:"breadcrumbs",itemScope:!0,itemType:"https://schema.org/BreadcrumbList"},t&&l.createElement(p,null),e.map(((t,n)=>{const a=n===e.length-1;return l.createElement(h,{key:n,active:a,index:n,addMicrodata:!!t.href},l.createElement(b,{href:t.href,isLast:a},t.label))})))):null}},4726:(e,t,n)=>{n.r(t),n.d(t,{default:()=>Q});var a=n(2784),l=n(328),r=n(6335);const s=a.createContext(null);function o(e){let{children:t,content:n}=e;const l=function(e){return(0,a.useMemo)((()=>({metadata:e.metadata,frontMatter:e.frontMatter,assets:e.assets,contentTitle:e.contentTitle,toc:e.toc})),[e])}(n);return a.createElement(s.Provider,{value:l},t)}function c(){const e=(0,a.useContext)(s);if(null===e)throw new r.i6("DocProvider");return e}function i(){var e;const{metadata:t,frontMatter:n,assets:r}=c();return a.createElement(l.d,{title:t.title,description:t.description,keywords:n.keywords,image:null!=(e=r.image)?e:n.image})}var d=n(6277),m=n(7963),u=n(2992);function v(){const{metadata:e}=c();return a.createElement(u.Z,{previous:e.previous,next:e.next})}var b=n(2269),h=n(8541),p=n(211),f=n(1077);function E(e){let{lastUpdatedAt:t,formattedLastUpdatedAt:n}=e;return a.createElement(f.Z,{id:"theme.lastUpdated.atDate",description:"The words used to describe on which date a page has been last updated",values:{date:a.createElement("b",null,a.createElement("time",{dateTime:new Date(1e3*t).toISOString()},n))}}," on {date}")}function g(e){let{lastUpdatedBy:t}=e;return a.createElement(f.Z,{id:"theme.lastUpdated.byUser",description:"The words used to describe by who the page has been last updated",values:{user:a.createElement("b",null,t)}}," by {user}")}function L(e){let{lastUpdatedAt:t,formattedLastUpdatedAt:n,lastUpdatedBy:l}=e;return a.createElement("span",{className:p.k.common.lastUpdated},a.createElement(f.Z,{id:"theme.lastUpdated.lastUpdatedAtBy",description:"The sentence used to display when a page has been last updated, and by who",values:{atDate:t&&n?a.createElement(E,{lastUpdatedAt:t,formattedLastUpdatedAt:n}):"",byUser:l?a.createElement(g,{lastUpdatedBy:l}):""}},"Last updated{atDate}{byUser}"),!1)}var N=n(5040),Z=n(2569);const k="lastUpdated_T23F";function _(e){return a.createElement("div",{className:(0,d.Z)(p.k.docs.docFooterTagsRow,"row margin-bottom--sm")},a.createElement("div",{className:"col"},a.createElement(Z.Z,e)))}function C(e){let{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:l,formattedLastUpdatedAt:r}=e;return a.createElement("div",{className:(0,d.Z)(p.k.docs.docFooterEditMetaRow,"row")},a.createElement("div",{className:"col"},t&&a.createElement(N.Z,{editUrl:t})),a.createElement("div",{className:(0,d.Z)("col",k)},(n||l)&&a.createElement(L,{lastUpdatedAt:n,formattedLastUpdatedAt:r,lastUpdatedBy:l})))}function H(){const{metadata:e}=c(),{editUrl:t,lastUpdatedAt:n,formattedLastUpdatedAt:l,lastUpdatedBy:r,tags:s}=e,o=s.length>0,i=!!(t||n||r);return o||i?a.createElement("footer",{className:(0,d.Z)(p.k.docs.docFooter,"docusaurus-mt-lg")},o&&a.createElement(_,{tags:s}),i&&a.createElement(C,{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:r,formattedLastUpdatedAt:l})):null}var T=n(8698),x=n(6986),U=n(7896);const A="tocCollapsibleButton_htYj",y="tocCollapsibleButtonExpanded_pAh7";function w(e){let{collapsed:t,...n}=e;return a.createElement("button",(0,U.Z)({type:"button"},n,{className:(0,d.Z)("clean-btn",A,!t&&y,n.className)}),a.createElement(f.Z,{id:"theme.TOCCollapsible.toggleButtonLabel",description:"The label used by the button on the collapsible TOC component"},"On this page"))}const M="tocCollapsible_O_Qc",I="tocCollapsibleContent_SlnY",B="tocCollapsibleExpanded_klrc";function S(e){let{toc:t,className:n,minHeadingLevel:l,maxHeadingLevel:r}=e;const{collapsed:s,toggleCollapsed:o}=(0,T.u)({initialState:!0});return a.createElement("div",{className:(0,d.Z)(M,!s&&B,n)},a.createElement(w,{collapsed:s,onClick:o}),a.createElement(T.z,{lazy:!0,className:I,collapsed:s},a.createElement(x.Z,{toc:t,minHeadingLevel:l,maxHeadingLevel:r})))}const O="tocMobile_tjDr";function V(){const{toc:e,frontMatter:t}=c();return a.createElement(S,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:(0,d.Z)(p.k.docs.docTocMobile,O)})}var D=n(8188);function R(){const{toc:e,frontMatter:t}=c();return a.createElement(D.Z,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:p.k.docs.docTocDesktop})}var P=n(3851),z=n(8632);function F(e){let{children:t}=e;const n=function(){const{metadata:e,frontMatter:t,contentTitle:n}=c();return t.hide_title||void 0!==n?null:e.title}();return a.createElement("div",{className:(0,d.Z)(p.k.docs.docMarkdown,"markdown")},n&&a.createElement("header",null,a.createElement(P.Z,{as:"h1"},n)),a.createElement(z.Z,null,t))}var j=n(7186);const J="docItemContainer_Rv5Z",Y="docItemCol_YAwJ";function q(e){let{children:t}=e;const n=function(){const{frontMatter:e,toc:t}=c(),n=(0,m.i)(),l=e.hide_table_of_contents,r=!l&&t.length>0;return{hidden:l,mobile:r?a.createElement(V,null):void 0,desktop:!r||"desktop"!==n&&"ssr"!==n?void 0:a.createElement(R,null)}}();return a.createElement("div",{className:"row"},a.createElement("div",{className:(0,d.Z)("col",!n.hidden&&Y)},a.createElement(b.Z,null),a.createElement("div",{className:J},a.createElement("article",null,a.createElement(j.Z,null),a.createElement(h.Z,null),n.mobile,a.createElement(F,null,t),a.createElement(H,null)),a.createElement(v,null))),n.desktop&&a.createElement("div",{className:"col col--3"},n.desktop))}function Q(e){const t="docs-doc-id-"+e.content.metadata.unversionedId,n=e.content;return a.createElement(o,{content:e.content},a.createElement(l.FG,{className:t},a.createElement(i,null),a.createElement(q,null,a.createElement(n,null))))}},2992:(e,t,n)=>{n.d(t,{Z:()=>o});var a=n(7896),l=n(2784),r=n(1077),s=n(7066);function o(e){const{previous:t,next:n}=e;return l.createElement("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,r.I)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages navigation",description:"The ARIA label for the docs pagination"})},t&&l.createElement(s.Z,(0,a.Z)({},t,{subLabel:l.createElement(r.Z,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc"},"Previous")})),n&&l.createElement(s.Z,(0,a.Z)({},n,{subLabel:l.createElement(r.Z,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc"},"Next"),isNext:!0})))}},8541:(e,t,n)=>{n.d(t,{Z:()=>c});var a=n(2784),l=n(6277),r=n(1077),s=n(211),o=n(5663);function c(e){let{className:t}=e;const n=(0,o.E)();return n.badge?a.createElement("span",{className:(0,l.Z)(t,s.k.docs.docVersionBadge,"badge badge--secondary")},a.createElement(r.Z,{id:"theme.docs.versionBadge.label",values:{versionLabel:n.label}},"Version: {versionLabel}")):null}},2269:(e,t,n)=>{n.d(t,{Z:()=>p});var a=n(2784),l=n(6277),r=n(7614),s=n(9817),o=n(1077),c=n(1215),i=n(211),d=n(7949),m=n(5663);const u={unreleased:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(o.Z,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is unreleased documentation for {siteTitle} {versionLabel} version.")},unmaintained:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(o.Z,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.")}};function v(e){const t=u[e.versionMetadata.banner];return a.createElement(t,e)}function b(e){let{versionLabel:t,to:n,onClick:l}=e;return a.createElement(o.Z,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:t,latestVersionLink:a.createElement("b",null,a.createElement(s.Z,{to:n,onClick:l},a.createElement(o.Z,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label"},"latest version")))}},"For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).")}function h(e){let{className:t,versionMetadata:n}=e;const{siteConfig:{title:s}}=(0,r.Z)(),{pluginId:o}=(0,c.gA)({failfast:!0}),{savePreferredVersionName:m}=(0,d.J)(o),{latestDocSuggestion:u,latestVersionSuggestion:h}=(0,c.Jo)(o),p=null!=u?u:(f=h).docs.find((e=>e.id===f.mainDocId));var f;return a.createElement("div",{className:(0,l.Z)(t,i.k.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert"},a.createElement("div",null,a.createElement(v,{siteTitle:s,versionMetadata:n})),a.createElement("div",{className:"margin-top--md"},a.createElement(b,{versionLabel:h.label,to:p.path,onClick:()=>m(h.name)})))}function p(e){let{className:t}=e;const n=(0,m.E)();return n.banner?a.createElement(h,{className:t,versionMetadata:n}):null}},8188:(e,t,n)=>{n.d(t,{Z:()=>c});var a=n(7896),l=n(2784),r=n(6277),s=n(6986);const o="tableOfContents_TN1Q";function c(e){let{className:t,...n}=e;return l.createElement("div",{className:(0,r.Z)(o,"thin-scrollbar",t)},l.createElement(s.Z,(0,a.Z)({},n,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}},6986:(e,t,n)=>{n.d(t,{Z:()=>b});var a=n(7896),l=n(2784),r=n(7683);function s(e){const t=e.map((e=>({...e,parentIndex:-1,children:[]}))),n=Array(7).fill(-1);t.forEach(((e,t)=>{const a=n.slice(2,e.level);e.parentIndex=Math.max(...a),n[e.level]=t}));const a=[];return t.forEach((e=>{const{parentIndex:n,...l}=e;n>=0?t[n].children.push(l):a.push(l)})),a}function o(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return t.flatMap((e=>{const t=o({toc:e.children,minHeadingLevel:n,maxHeadingLevel:a});return function(e){return e.level>=n&&e.level<=a}(e)?[{...e,children:t}]:t}))}function c(e){const t=e.getBoundingClientRect();return t.top===t.bottom?c(e.parentNode):t}function i(e,t){var n;let{anchorTopOffset:a}=t;const l=e.find((e=>c(e).top>=a));if(l){var r;return function(e){return e.top>0&&e.bottom<window.innerHeight/2}(c(l))?l:null!=(r=e[e.indexOf(l)-1])?r:null}return null!=(n=e[e.length-1])?n:null}function d(){const e=(0,l.useRef)(0),{navbar:{hideOnScroll:t}}=(0,r.L)();return(0,l.useEffect)((()=>{e.current=t?0:document.querySelector(".navbar").clientHeight}),[t]),e}function m(e){const t=(0,l.useRef)(void 0),n=d();(0,l.useEffect)((()=>{if(!e)return()=>{};const{linkClassName:a,linkActiveClassName:l,minHeadingLevel:r,maxHeadingLevel:s}=e;function o(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(a),o=function(e){let{minHeadingLevel:t,maxHeadingLevel:n}=e;const a=[];for(let l=t;l<=n;l+=1)a.push("h"+l+".anchor");return Array.from(document.querySelectorAll(a.join()))}({minHeadingLevel:r,maxHeadingLevel:s}),c=i(o,{anchorTopOffset:n.current}),d=e.find((e=>c&&c.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e)));e.forEach((e=>{!function(e,n){n?(t.current&&t.current!==e&&t.current.classList.remove(l),e.classList.add(l),t.current=e):e.classList.remove(l)}(e,e===d)}))}return document.addEventListener("scroll",o),document.addEventListener("resize",o),o(),()=>{document.removeEventListener("scroll",o),document.removeEventListener("resize",o)}}),[e,n])}function u(e){let{toc:t,className:n,linkClassName:a,isChild:r}=e;return t.length?l.createElement("ul",{className:r?void 0:n},t.map((e=>l.createElement("li",{key:e.id},l.createElement("a",{href:"#"+e.id,className:null!=a?a:void 0,dangerouslySetInnerHTML:{__html:e.value}}),l.createElement(u,{isChild:!0,toc:e.children,className:n,linkClassName:a}))))):null}const v=l.memo(u);function b(e){let{toc:t,className:n="table-of-contents table-of-contents__left-border",linkClassName:c="table-of-contents__link",linkActiveClassName:i,minHeadingLevel:d,maxHeadingLevel:u,...b}=e;const h=(0,r.L)(),p=null!=d?d:h.tableOfContents.minHeadingLevel,f=null!=u?u:h.tableOfContents.maxHeadingLevel,E=function(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return(0,l.useMemo)((()=>o({toc:s(t),minHeadingLevel:n,maxHeadingLevel:a})),[t,n,a])}({toc:t,minHeadingLevel:p,maxHeadingLevel:f});return m((0,l.useMemo)((()=>{if(c&&i)return{linkClassName:c,linkActiveClassName:i,minHeadingLevel:p,maxHeadingLevel:f}}),[c,i,p,f])),l.createElement(v,(0,a.Z)({toc:E,className:n,linkClassName:c},b))}}}]); \ No newline at end of file diff --git a/docs/assets/js/1be78505.b84d86db.js b/docs/assets/js/1be78505.b84d86db.js deleted file mode 100644 index 1ee29e62..00000000 --- a/docs/assets/js/1be78505.b84d86db.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9514,2006],{4959:(e,t,n)=>{n.r(t),n.d(t,{default:()=>Ce});var a=n(2784),l=n(6277),o=n(328),c=n(211),r=n(4855),i=n(4925),s=n(5663),d=n(4228),m=n(9991),u=n(1077),b=n(4126),p=n(2105);const h="backToTopButton_z1FD",E="backToTopButtonShow_w1wE";function f(){const{shown:e,scrollToTop:t}=function(e){let{threshold:t}=e;const[n,l]=(0,a.useState)(!1),o=(0,a.useRef)(!1),{startScroll:c,cancelScroll:r}=(0,b.Ct)();return(0,b.RF)(((e,n)=>{let{scrollY:a}=e;const c=null==n?void 0:n.scrollY;c&&(o.current?o.current=!1:a>=c?(r(),l(!1)):a<t?l(!1):a+window.innerHeight<document.documentElement.scrollHeight&&l(!0))})),(0,p.S)((e=>{e.location.hash&&(o.current=!0,l(!1))})),{shown:n,scrollToTop:()=>c(0)}}({threshold:300});return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.BackToTopButton.buttonAriaLabel",message:"Scroll back to top",description:"The ARIA label for the back to top button"}),className:(0,l.Z)("clean-btn",c.k.common.backToTopButton,h,e&&E),type:"button",onClick:t})}var v=n(3181),g=n(7963),_=n(7683),k=n(1881),C=n(7896);function I(e){return a.createElement("svg",(0,C.Z)({width:"20",height:"20","aria-hidden":"true"},e),a.createElement("g",{fill:"#7a7a7a"},a.createElement("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),a.createElement("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})))}const N="collapseSidebarButton_Ftvb",S="collapseSidebarButtonIcon_c4WT";function Z(e){let{onClick:t}=e;return a.createElement("button",{type:"button",title:(0,u.I)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,l.Z)("button button--secondary button--outline",N),onClick:t},a.createElement(I,{className:S}))}var T=n(3717),y=n(6335);const x=Symbol("EmptyContext"),w=a.createContext(x);function L(e){let{children:t}=e;const[n,l]=(0,a.useState)(null),o=(0,a.useMemo)((()=>({expandedItem:n,setExpandedItem:l})),[n]);return a.createElement(w.Provider,{value:o},t)}var F=n(8698),A=n(7661),M=n(9817),B=n(9741);function H(e){let{categoryLabel:t,onClick:n}=e;return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel",message:"Toggle the collapsible sidebar category '{label}'",description:"The ARIA label to toggle the collapsible sidebar category"},{label:t}),type:"button",className:"clean-btn menu__caret",onClick:n})}function P(e){let{item:t,onItemClick:n,activePath:o,level:i,index:s,...d}=e;const{items:m,label:u,collapsible:b,className:p,href:h}=t,{docs:{sidebar:{autoCollapseCategories:E}}}=(0,_.L)(),f=function(e){const t=(0,B.Z)();return(0,a.useMemo)((()=>e.href?e.href:!t&&e.collapsible?(0,r.Wl)(e):void 0),[e,t])}(t),v=(0,r._F)(t,o),g=(0,A.Mg)(h,o),{collapsed:k,setCollapsed:I}=(0,F.u)({initialState:()=>!!b&&(!v&&t.collapsed)}),{expandedItem:N,setExpandedItem:S}=function(){const e=(0,a.useContext)(w);if(e===x)throw new y.i6("DocSidebarItemsExpandedStateProvider");return e}(),Z=function(e){void 0===e&&(e=!k),S(e?null:s),I(e)};return function(e){let{isActive:t,collapsed:n,updateCollapsed:l}=e;const o=(0,y.D9)(t);(0,a.useEffect)((()=>{t&&!o&&n&&l(!1)}),[t,o,n,l])}({isActive:v,collapsed:k,updateCollapsed:Z}),(0,a.useEffect)((()=>{b&&N&&N!==s&&E&&I(!0)}),[b,N,s,I,E]),a.createElement("li",{className:(0,l.Z)(c.k.docs.docSidebarItemCategory,c.k.docs.docSidebarItemCategoryLevel(i),"menu__list-item",{"menu__list-item--collapsed":k},p)},a.createElement("div",{className:(0,l.Z)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":g})},a.createElement(M.Z,(0,C.Z)({className:(0,l.Z)("menu__link",{"menu__link--sublist":b,"menu__link--sublist-caret":!h&&b,"menu__link--active":v}),onClick:b?e=>{null==n||n(t),h?Z(!1):(e.preventDefault(),Z())}:()=>{null==n||n(t)},"aria-current":g?"page":void 0,"aria-expanded":b?!k:void 0,href:b?null!=f?f:"#":f},d),u),h&&b&&a.createElement(H,{categoryLabel:u,onClick:e=>{e.preventDefault(),Z()}})),a.createElement(F.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:k},a.createElement(V,{items:m,tabIndex:k?-1:0,onItemClick:n,activePath:o,level:i+1})))}var W=n(1344),D=n(4442);const R="menuExternalLink_xK2O";function q(e){let{item:t,onItemClick:n,activePath:o,level:i,index:s,...d}=e;const{href:m,label:u,className:b}=t,p=(0,r._F)(t,o),h=(0,W.Z)(m);return a.createElement("li",{className:(0,l.Z)(c.k.docs.docSidebarItemLink,c.k.docs.docSidebarItemLinkLevel(i),"menu__list-item",b),key:u},a.createElement(M.Z,(0,C.Z)({className:(0,l.Z)("menu__link",!h&&R,{"menu__link--active":p}),"aria-current":p?"page":void 0,to:m},h&&{onClick:n?()=>n(t):void 0},d),u,!h&&a.createElement(D.Z,null)))}const z="menuHtmlItem_anEq";function O(e){let{item:t,level:n,index:o}=e;const{value:r,defaultStyle:i,className:s}=t;return a.createElement("li",{className:(0,l.Z)(c.k.docs.docSidebarItemLink,c.k.docs.docSidebarItemLinkLevel(n),i&&[z,"menu__list-item"],s),key:o,dangerouslySetInnerHTML:{__html:r}})}function K(e){let{item:t,...n}=e;switch(t.type){case"category":return a.createElement(P,(0,C.Z)({item:t},n));case"html":return a.createElement(O,(0,C.Z)({item:t},n));default:return a.createElement(q,(0,C.Z)({item:t},n))}}function U(e){let{items:t,...n}=e;return a.createElement(L,null,t.map(((e,t)=>a.createElement(K,(0,C.Z)({key:t,item:e,index:t},n)))))}const V=(0,a.memo)(U),Y="menu_qiME",j="menuWithAnnouncementBar_hRfJ";function J(e){let{path:t,sidebar:n,className:o}=e;const r=function(){const{isActive:e}=(0,T.nT)(),[t,n]=(0,a.useState)(e);return(0,b.RF)((t=>{let{scrollY:a}=t;e&&n(0===a)}),[e]),e&&t}();return a.createElement("nav",{className:(0,l.Z)("menu thin-scrollbar",Y,r&&j,o)},a.createElement("ul",{className:(0,l.Z)(c.k.docs.docSidebarMenu,"menu__list")},a.createElement(V,{items:n,activePath:t,level:1})))}const G="sidebar_vJCc",Q="sidebarWithHideableNavbar_Fo4g",X="sidebarHidden_vBKa",$="sidebarLogo_nlll";function ee(e){let{path:t,sidebar:n,onCollapse:o,isHidden:c}=e;const{navbar:{hideOnScroll:r},docs:{sidebar:{hideable:i}}}=(0,_.L)();return a.createElement("div",{className:(0,l.Z)(G,r&&Q,c&&X)},r&&a.createElement(k.Z,{tabIndex:-1,className:$}),a.createElement(J,{path:t,sidebar:n}),i&&a.createElement(Z,{onClick:o}))}const te=a.memo(ee);var ne=n(7548),ae=n(7136);const le=e=>{let{sidebar:t,path:n}=e;const o=(0,ae.e)();return a.createElement("ul",{className:(0,l.Z)(c.k.docs.docSidebarMenu,"menu__list")},a.createElement(V,{items:t,activePath:n,onItemClick:e=>{"category"===e.type&&e.href&&o.toggle(),"link"===e.type&&o.toggle()},level:1}))};function oe(e){return a.createElement(ne.Zo,{component:le,props:e})}const ce=a.memo(oe);function re(e){const t=(0,g.i)(),n="desktop"===t||"ssr"===t,l="mobile"===t;return a.createElement(a.Fragment,null,n&&a.createElement(te,e),l&&a.createElement(ce,e))}const ie="expandButton_qIqc",se="expandButtonIcon_aOpf";function de(e){let{toggleSidebar:t}=e;return a.createElement("div",{className:ie,title:(0,u.I)({id:"theme.docs.sidebar.expandButtonTitle",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.expandButtonAriaLabel",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),tabIndex:0,role:"button",onKeyDown:t,onClick:t},a.createElement(I,{className:se}))}const me="docSidebarContainer_aIKW",ue="docSidebarContainerHidden_UIq3";function be(e){var t;let{children:n}=e;const l=(0,d.V)();return a.createElement(a.Fragment,{key:null!=(t=null==l?void 0:l.name)?t:"noSidebar"},n)}function pe(e){let{sidebar:t,hiddenSidebarContainer:n,setHiddenSidebarContainer:o}=e;const{pathname:r}=(0,v.TH)(),[i,s]=(0,a.useState)(!1),d=(0,a.useCallback)((()=>{i&&s(!1),o((e=>!e))}),[o,i]);return a.createElement("aside",{className:(0,l.Z)(c.k.docs.docSidebarContainer,me,n&&ue),onTransitionEnd:e=>{e.currentTarget.classList.contains(me)&&n&&s(!0)}},a.createElement(be,null,a.createElement(re,{sidebar:t,path:r,onCollapse:d,isHidden:i})),i&&a.createElement(de,{toggleSidebar:d}))}const he={docMainContainer:"docMainContainer_fv3b",docMainContainerEnhanced:"docMainContainerEnhanced_wOQt",docItemWrapperEnhanced:"docItemWrapperEnhanced_DUiu"};function Ee(e){let{hiddenSidebarContainer:t,children:n}=e;const o=(0,d.V)();return a.createElement("main",{className:(0,l.Z)(he.docMainContainer,(t||!o)&&he.docMainContainerEnhanced)},a.createElement("div",{className:(0,l.Z)("container padding-top--md padding-bottom--lg",he.docItemWrapper,t&&he.docItemWrapperEnhanced)},n))}const fe="docPage_pOTq",ve="docsWrapper_BqXd";function ge(e){let{children:t}=e;const n=(0,d.V)(),[l,o]=(0,a.useState)(!1);return a.createElement(m.Z,{wrapperClassName:ve},a.createElement(f,null),a.createElement("div",{className:fe},n&&a.createElement(pe,{sidebar:n.items,hiddenSidebarContainer:l,setHiddenSidebarContainer:o}),a.createElement(Ee,{hiddenSidebarContainer:l},t)))}var _e=n(2006),ke=n(4390);function Ce(e){const{versionMetadata:t}=e,n=(0,r.hI)(e);if(!n)return a.createElement(_e.default,null);const{docElement:m,sidebarName:u,sidebarItems:b}=n;return a.createElement(a.Fragment,null,a.createElement(ke.Z,{version:t.version,tag:(0,i.os)(t.pluginId,t.version)}),a.createElement(o.FG,{className:(0,l.Z)(c.k.wrapper.docsPages,c.k.page.docsDocPage,e.versionMetadata.className)},a.createElement(s.q,{version:t},a.createElement(d.b,{name:u,items:b},a.createElement(ge,null,m)))))}},2006:(e,t,n)=>{n.r(t),n.d(t,{default:()=>r});var a=n(2784),l=n(1077),o=n(328),c=n(9991);function r(){return a.createElement(a.Fragment,null,a.createElement(o.d,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(c.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}}}]); \ No newline at end of file diff --git a/docs/assets/js/1df93b7f.019f5c69.js b/docs/assets/js/1df93b7f.019f5c69.js deleted file mode 100644 index 3cf62995..00000000 --- a/docs/assets/js/1df93b7f.019f5c69.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3237],{6376:(e,t,c)=>{c.d(t,{Z:()=>r});var a,n=c(2784);function l(){return l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var c=arguments[t];for(var a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a])}return e},l.apply(this,arguments)}const r=e=>{let{title:t,titleId:c,...r}=e;return n.createElement("svg",l({width:"512pt",height:"512pt",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg","aria-labelledby":c},r),t?n.createElement("title",{id:c},t):null,a||(a=n.createElement("g",{fill:"#0bafb9"},n.createElement("path",{d:"M488.45 171.01c-2.559-7.168-4.61-13.824-7.168-20.48-9.727-29.184-17.922-54.785-39.426-65.535-13.824-6.656-31.23-6.144-53.762 1.535-4.61 1.535-8.703 3.586-12.801 5.633h-103.93c-4.61-2.047-8.703-4.098-12.801-5.633-22.016-7.68-39.938-8.191-53.762-1.535-21.504 10.754-29.695 35.84-39.426 65.535-2.047 6.656-4.61 13.824-6.656 20.48-3.586 10.754-6.656 21.504-8.192 31.742h19.97c1.535-8.191 4.097-16.895 7.167-25.09 2.559-7.168 4.61-14.336 7.168-20.992 8.192-25.09 15.36-46.594 29.184-53.762 8.704-4.098 21.504-3.586 38.398 2.559 4.098 1.535 8.192 3.07 12.801 5.632 1.536.512 3.07 1.024 4.61 1.024h108.54c1.535 0 3.07-.512 4.61-1.024 4.609-2.047 8.702-4.097 12.8-5.632 16.895-6.145 29.695-6.657 38.398-2.559 13.824 7.168 20.992 28.672 29.184 53.762 2.047 6.656 4.61 13.824 7.168 20.992 6.145 17.922 9.727 36.352 9.727 52.734 0 5.633-.512 11.266-1.024 16.895-3.07 17.922-10.238 30.207-20.48 33.793-10.238 3.586-24.062-1.023-37.375-13.312-15.359-13.824-28.672-34.816-37.375-59.391-1.535-4.098-5.12-6.656-9.214-6.656l-52.223.004c8.191 5.12 14.336 11.777 19.969 19.969h24.574c9.726 24.574 24.062 46.078 40.449 60.93 14.336 12.8 29.184 19.457 43.008 19.457 4.609 0 9.726-1.024 13.824-2.559 17.922-6.144 29.695-24.062 33.793-49.664 3.07-23.043 0-51.203-9.727-78.852z"}),n.createElement("path",{d:"M420.35 135.17c0 6.785-5.5 12.289-12.289 12.289-6.785 0-12.285-5.504-12.285-12.289s5.5-12.289 12.285-12.289c6.79 0 12.289 5.504 12.289 12.289M444.93 159.74c0 6.79-5.504 12.289-12.289 12.289s-12.289-5.5-12.289-12.289c0-6.785 5.504-12.285 12.289-12.285s12.289 5.5 12.289 12.285M395.78 159.74c0 6.79-5.504 12.289-12.289 12.289s-12.289-5.5-12.289-12.289c0-6.785 5.504-12.285 12.289-12.285s12.289 5.5 12.289 12.285M420.35 184.32c0 6.785-5.5 12.289-12.289 12.289-6.785 0-12.285-5.504-12.285-12.289s5.5-12.289 12.285-12.289c6.79 0 12.289 5.504 12.289 12.289M247.81 128h-14.336c-1.535 0-2.559 1.023-2.559 2.559v18.945H211.97c-1.535 0-2.558 1.023-2.558 2.558v14.336c0 1.535 1.023 2.559 2.558 2.559l18.43.004v18.945c0 1.535 1.024 2.558 2.559 2.558h14.336c1.535 0 2.559-1.023 2.559-2.558V168.96h18.945c1.535 0 2.558-1.024 2.558-2.559v-14.336c0-1.535-1.023-2.558-2.558-2.558h-18.945v-18.945c.515-1.54-1.02-2.563-2.043-2.563zM346.62 280.06c-9.73-29.184-17.922-54.781-39.426-65.535-13.824-6.656-31.23-6.144-53.762 1.535-4.61 1.535-8.703 3.586-12.801 5.633h-103.93c-4.61-2.047-8.703-4.098-12.801-5.633-22.016-7.68-39.938-8.191-53.762-1.535-22.012 10.754-30.203 36.352-39.422 65.535-2.047 6.656-4.61 13.824-6.656 20.48-9.727 27.648-13.312 55.297-9.215 78.848 4.098 25.602 15.871 43.008 33.793 49.664 17.922 6.144 37.887 0 57.344-16.895 16.383-14.848 30.207-36.352 40.449-60.93h86.016c9.727 25.09 24.062 46.078 40.449 60.93 14.336 12.801 29.184 19.457 43.008 19.457 4.61 0 9.727-1.023 14.336-2.559 17.922-6.144 29.695-24.062 33.793-49.664 3.586-23.55.512-51.71-9.215-78.848-3.59-6.66-6.148-13.828-8.195-20.484zm-3.074 96.77c-3.07 17.922-10.238 30.207-20.992 33.793-10.238 3.586-24.062-1.023-37.375-13.312-15.36-13.824-28.672-34.816-37.375-59.391-1.535-4.098-5.121-6.656-9.215-6.656h-99.84c-4.098 0-8.191 3.07-9.215 7.168-8.703 24.574-22.016 45.566-37.375 58.879-13.312 12.289-27.137 16.895-37.375 13.312-10.238-3.586-17.922-15.871-20.992-33.793-3.07-20.48 0-45.055 8.703-69.121 2.559-7.168 5.121-14.336 7.168-20.992 8.192-25.09 15.36-46.594 29.184-53.762 8.703-4.098 21.504-3.586 38.398 2.558 4.098 1.536 8.192 3.07 12.801 5.633 1.535.512 3.07 1.024 4.61 1.024h108.54c1.535 0 3.07-.512 4.61-1.024 4.608-2.047 8.702-4.097 12.8-5.633 16.895-6.144 29.695-6.656 38.398-2.558 13.824 7.168 20.992 28.672 29.184 53.762 2.047 6.656 4.61 13.824 7.168 20.992 8.188 24.066 11.262 48.64 8.188 69.12z"}),n.createElement("path",{d:"M285.7 264.7c0 6.79-5.5 12.289-12.289 12.289-6.785 0-12.285-5.5-12.285-12.289 0-6.785 5.5-12.289 12.285-12.289 6.79 0 12.289 5.504 12.289 12.289M310.27 289.28c0 6.785-5.504 12.285-12.289 12.285s-12.289-5.5-12.289-12.285c0-6.79 5.504-12.289 12.289-12.289s12.289 5.5 12.289 12.289M260.61 289.28c0 6.785-5.504 12.285-12.289 12.285s-12.289-5.5-12.289-12.285c0-6.79 5.504-12.289 12.289-12.289s12.289 5.5 12.289 12.289M285.7 313.86c0 6.785-5.5 12.289-12.289 12.289-6.785 0-12.285-5.504-12.285-12.289s5.5-12.289 12.285-12.289c6.79 0 12.289 5.504 12.289 12.289M134.66 279.55h-18.945v-18.945c0-1.535-1.023-2.559-2.559-2.559H98.82c-1.535 0-2.558 1.024-2.558 2.559v18.945H76.805c-1.535 0-2.559 1.023-2.559 2.559v14.336c0 1.535 1.024 2.558 2.559 2.558H95.75v18.945c0 1.535 1.023 2.559 2.558 2.559l14.336.004c1.536 0 2.559-1.024 2.559-2.559v-18.945h18.945c1.535 0 2.559-1.023 2.559-2.558v-14.336c.511-1.54-.512-2.563-2.047-2.563z"}))))}},1237:(e,t,c)=>{c.d(t,{Z:()=>r});var a,n=c(2784);function l(){return l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var c=arguments[t];for(var a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a])}return e},l.apply(this,arguments)}const r=e=>{let{title:t,titleId:c,...r}=e;return n.createElement("svg",l({width:"512pt",height:"512pt",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg","aria-labelledby":c},r),t?n.createElement("title",{id:c},t):null,a||(a=n.createElement("path",{d:"M426.55 289.12c45.781-58.652 41.836-141.86 41.629-145.65-.348-6.34-5.41-11.398-11.75-11.75-3.25-.168-65.484-3.125-120.17 25.332-13.176-68.664-69.258-119.64-71.949-122.04-4.73-4.234-11.887-4.234-16.613 0-2.691 2.406-58.77 53.379-71.949 122.04-54.68-28.453-116.91-25.5-120.17-25.332a12.457 12.457 0 0 0-11.75 11.75c-.21 3.785-4.156 87 41.625 145.65-29.32 14.344-48.66 33.219-49.941 34.492a12.463 12.463 0 0 0-3.68 8.84c0 3.316 1.324 6.5 3.68 8.84 2.086 2.066 51.758 50.664 114.63 50.664 17.254 0 33.48-3.696 48.035-9.047-1.059 4.605-1.68 9.355-1.68 14.238 0 45.098 50.41 79.473 52.56 80.91a12.453 12.453 0 0 0 13.89 0c2.144-1.442 52.559-35.816 52.559-80.91 0-4.875-.617-9.617-1.672-14.215 14.551 5.347 30.785 9.023 48.027 9.023 62.875 0 112.55-48.594 114.63-50.664a12.463 12.463 0 0 0 3.68-8.84c0-3.316-1.324-6.5-3.68-8.84-1.281-1.269-20.621-20.147-49.941-34.491zm16.781-132.54c-.71 25.93-6.703 88.875-46.297 128.47-34.273 34.273-86.297 43.348-116.4 45.633a166.572 166.572 0 0 0-11.566-9.941c2.063-29.816 10.82-82.926 45.773-117.88 39.48-39.477 102.55-45.535 128.49-46.281zm-187.32-94.73c16.328 17.246 51.391 59.602 57.195 109.62-5.555 4.16-10.918 8.719-15.98 13.781-20.621 20.621-33.359 46.02-41.23 70.438-7.871-24.418-20.605-49.816-41.227-70.438-5.063-5.062-10.422-9.617-15.973-13.777 5.82-49.891 40.898-92.348 57.215-109.62zm-187.33 94.73c25.969.75 89.012 6.816 128.47 46.281h.004c34.855 34.855 43.645 88.059 45.742 117.91a166.149 166.149 0 0 0-11.535 9.918c-30.113-2.29-82.133-11.363-116.4-45.633-39.465-39.469-45.527-102.51-46.277-128.47zm81.453 210.46c-37.426 0-70.648-21.891-86.773-34.594 8.871-6.988 22.973-16.715 40.043-24.09 30.855 27.496 70.426 39.375 101.64 44.41-15.723 7.879-34.684 14.273-54.906 14.273zm105.84 85.125c-13.625-11.078-34.578-32.848-34.578-55.027 0-22.066 20.992-43.906 34.609-55.027 13.625 11.078 34.578 32.848 34.578 55.027 0 22.07-20.992 43.906-34.609 55.027zm105.88-85.125c-20.234 0-39.211-6.406-54.941-14.289 31.215-5.031 70.75-16.914 101.62-44.414 17.094 7.379 31.211 17.117 40.09 24.109-16.129 12.707-49.352 34.594-86.773 34.594z",fill:"#0bafb9"})))}},544:(e,t,c)=>{c.d(t,{Z:()=>r});var a,n=c(2784);function l(){return l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var c=arguments[t];for(var a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a])}return e},l.apply(this,arguments)}const r=e=>{let{title:t,titleId:c,...r}=e;return n.createElement("svg",l({width:"512pt",height:"512pt",viewBox:"0 0 512 512",xmlns:"http://www.w3.org/2000/svg",fill:"#0CAFBA","aria-labelledby":c},r),t?n.createElement("title",{id:c},t):null,a||(a=n.createElement("path",{d:"M354.3 258.56H156.67c-2.559 0-5.121-1.535-6.656-4.098-1.535-2.558-1.535-5.12 0-7.68l98.816-171.01c1.535-2.558 4.097-3.585 6.656-3.585 2.559 0 5.121 1.535 6.656 3.586l98.816 171.01c1.535 2.558 1.535 5.12 0 7.68-1.535 2.562-4.097 4.097-6.656 4.097zm-184.32-15.359h171.01l-85.504-147.97zM419.33 439.81H265.22c-4.098 0-7.68-3.586-7.68-7.68V278.02c0-4.097 3.586-7.68 7.68-7.68h154.11c4.098 0 7.68 3.586 7.68 7.68v154.11c0 4.094-3.582 7.68-7.68 7.68zM272.9 424.451h138.75v-138.75H272.9zM156.67 439.3c-46.594 0-84.48-37.887-84.48-84.48s37.887-84.48 84.48-84.48 84.48 37.887 84.48 84.48c0 46.59-37.887 84.48-84.48 84.48zm0-153.6c-37.887 0-69.121 31.23-69.121 69.121 0 37.887 31.23 69.121 69.121 69.121 37.887 0 69.121-31.23 69.121-69.121 0-37.887-31.234-69.121-69.121-69.121z"})))}},3593:(e,t,c)=>{c.r(t),c.d(t,{default:()=>f});var a=c(2784),n=c(6277),l=c(9817),r=c(7614),s=c(9991),i=c(7896);const o="features_t9lD",m="featureSvg_GfXr",u=[{title:"Simple, powerful, scalable paradigm",Svg:c(544).Z,description:a.createElement(a.Fragment,null,'Create "single page app" user experiences with the ease of server-rendered HTML.')},{title:"Native Real-time & multi-player support",Svg:c(6376).Z,description:a.createElement(a.Fragment,null,"Easily update the UI of any or all connected users with built-in support for Pub/Sub.")},{title:"No boilerplate & no reinventing the wheel",Svg:c(1237).Z,description:a.createElement(a.Fragment,null,'No "client-side routing" or "state management"; no REST or GraphQL APIs; No BS, just GSD-nirvana.')}];function d(e){let{title:t,Svg:c,description:n}=e;return a.createElement("div",{className:"grid grid-cols-1 justify-center"},a.createElement("div",{className:"flex justify-center"},a.createElement(c,{className:m,role:"img"})),a.createElement("h3",{className:"text-center text-2xl font-brand text-brand"},t),a.createElement("div",{className:"flex justify-center"},a.createElement("p",{className:"text-center mt-4 md:w-full w-3/4"},n)))}function h(){return a.createElement("section",{className:o},a.createElement("div",{className:"container"},a.createElement("div",{className:"grid grid-cols-1 md:grid-cols-3 justify-center gap-y-10 md:gap-x-12"},u.map(((e,t)=>a.createElement(d,(0,i.Z)({key:t},e)))))))}const p="heroBanner_qdFl",v="buttons_AeoN";function g(){const{siteConfig:e}=(0,r.Z)();return a.createElement("header",{className:(0,n.Z)("hero hero--primary",p)},a.createElement("div",{className:"container"},a.createElement("h1",{className:"hero__title font-brand "},a.createElement("span",{className:"font-semibold"},"LiveView"),a.createElement("span",{className:"font-thin"},"JS")),a.createElement("p",{className:"hero__subtitle"},e.tagline),a.createElement("div",{className:v},a.createElement(l.Z,{className:"mt-4 button button--secondary button--lg",to:"/docs/category/quick-starts"},"Quick Start \u2192"))))}function f(){const{siteConfig:e}=(0,r.Z)();return a.createElement(s.Z,{title:e.title,description:e.tagline},a.createElement(g,null),a.createElement("main",null,a.createElement(h,null),a.createElement("section",{className:"bg-brand py-8"},a.createElement("div",{className:"flex justify-center"},a.createElement(l.Z,{className:"button button--secondary button--lg",to:"/docs/category/overview"},"Learn More \u2192")))))}}}]); \ No newline at end of file diff --git a/docs/assets/js/1fdf7751.913e1f07.js b/docs/assets/js/1fdf7751.913e1f07.js deleted file mode 100644 index 8673a592..00000000 --- a/docs/assets/js/1fdf7751.913e1f07.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7800],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var r=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),l=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=l(e.components);return r.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=l(n),h=a,v=d["".concat(p,".").concat(h)]||d[h]||u[h]||i;return n?r.createElement(v,o(o({ref:t},c),{},{components:n})):r.createElement(v,o({ref:t},c))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=d;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:a,o[1]=s;for(var l=2;l<i;l++)o[l]=n[l];return r.createElement.apply(null,o)}return r.createElement.apply(null,n)}d.displayName="MDXCreateElement"},633:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var r=n(7896),a=(n(2784),n(876));const i={sidebar_position:3},o="Packages & Runtimes",s={unversionedId:"overview/runtimes",id:"overview/runtimes",title:"Packages & Runtimes",description:"LiveViewJS is written in Typescript and runs on both NodeJS and Deno.",source:"@site/docs/01-overview/runtimes.md",sourceDirName:"01-overview",slug:"/overview/runtimes",permalink:"/docs/overview/runtimes",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"LiveView Paradigm",permalink:"/docs/overview/paradigm"},next:{title:"Getting Involved",permalink:"/docs/overview/feedback"}},p={},l=[{value:"NodeJS and Deno Differences",id:"nodejs-and-deno-differences",level:2},{value:"Other Webservers?",id:"other-webservers",level:2},{value:"What about Bun?",id:"what-about-bun",level:2},{value:"LiveViewJS Packages",id:"liveviewjs-packages",level:2}],c={toc:l};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"packages--runtimes"},"Packages & Runtimes"),(0,a.kt)("p",null,"LiveViewJS is written in Typescript and runs on both ",(0,a.kt)("a",{parentName:"p",href:"https://nodejs.org/en/"},"NodeJS")," and ",(0,a.kt)("a",{parentName:"p",href:"https://deno.land/"},"Deno"),".\nWe've abstracted away APIs specific to each runtime so that you can write your code once and run it on both\nruntimes, assuming you don't use any runtime-specific APIs. That said, most developers are targeting one or the other, and\nwe've made it easy to use ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," with either runtime."),(0,a.kt)("h2",{id:"nodejs-and-deno-differences"},"NodeJS and Deno Differences"),(0,a.kt)("p",null,"As mentioned, we've worked hard to abstract away the differences between NodeJS and Deno so that you can write your code\nonce and run it on both runtimes. The only major difference between the two is with the following:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Pub/Sub - NodeJS has a Redis implementation for multi-process pub/sub while Deno uses\n",(0,a.kt)("a",{parentName:"li",href:"https://deno.com/deploy/docs/runtime-broadcast-channel"},"BroadcastChannel")," for multi-process pub/sub. Out of the box,\nboth NodeJS and Deno use the same single process EventEmitter-based implementation."),(0,a.kt)("li",{parentName:"ul"},"HTTP Server Libraries - With NodeJS, we ship with support for ",(0,a.kt)("a",{parentName:"li",href:"https://expressjs.com/"},"Express"),". Deno's HTTP server\nframework is called ",(0,a.kt)("a",{parentName:"li",href:"https://deno.land/x/oak@v11.1.0"},"Oak"),", and we ship with support for that. Theoretically, any HTTP\nserver framework that supports websockets should work with LiveViewJS. We've only tested with Express and Oak, but we'd\nlove to hear about your experiences with other frameworks and help support your framework of choice.")),(0,a.kt)("h2",{id:"other-webservers"},"Other Webservers?"),(0,a.kt)("p",null,"We've built out support for Express and Oak, but theoretically, any javascript HTTP webserver with support for websockets\nshould work with LiveViewJS. See ",(0,a.kt)("a",{parentName:"p",href:"/docs/webserver-integration/support-webserver-x"},"webserver integration")," for more\ndetails on integrating with your webserver of choice."),(0,a.kt)("h2",{id:"what-about-bun"},"What about Bun?"),(0,a.kt)("p",null,"LiveViewJS also runs on ",(0,a.kt)("a",{parentName:"p",href:"https://bun.sh/"},"Bun"),". We basically ran the NodeJS (express) version on Bun, and it works great!\n",(0,a.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/issues"},"Let us know")," if you run into any issues running ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," on Bun."),(0,a.kt)("h2",{id:"liveviewjs-packages"},"LiveViewJS Packages"),(0,a.kt)("p",null,"LiveViewJS is broken up into the following packages:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"liveviewjs")," ",(0,a.kt)("a",{parentName:"li",href:"https://www.npmjs.com/package/liveviewjs"},"Node")," and ",(0,a.kt)("a",{parentName:"li",href:"https://deno.land/x/liveviewjs"},"Deno")," - The core\npackage that contains the ",(0,a.kt)("strong",{parentName:"li"},"LiveViewJS")," core server code. This package is runtime agnostic and can be used with\neither NodeJS or Deno."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"@liveviewjs/examples")," ",(0,a.kt)("a",{parentName:"li",href:"https://www.npmjs.com/package/@liveviewjs/examples"},"Node")," or\n",(0,a.kt)("a",{parentName:"li",href:"https://deno.land/x/liveviewjs/packages/examples/mod.ts"},"Deno")," - The package contains all the example LiveViews that\ncan be run on either NodeJS or Deno. This package is runtime agnostic and can be used with either NodeJS or Deno."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"@liveviewjs/express")," ",(0,a.kt)("a",{parentName:"li",href:"https://www.npmjs.com/package/@liveviewjs/express"},"Node")," - The Express package that contains\nthe Express server integration code. This package is used to integrate ",(0,a.kt)("strong",{parentName:"li"},"LiveViewJS")," with Express (NodeJS)."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"https://deno.land/x/liveviewjs@VERSION/packages/deno/mod.ts")," - The Deno package that contains the Oak server\nintegration code. This package is used to integrate ",(0,a.kt)("strong",{parentName:"li"},"LiveViewJS")," with Oak (Deno).")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/2006.75ebd0c6.js b/docs/assets/js/2006.75ebd0c6.js deleted file mode 100644 index b9444ce2..00000000 --- a/docs/assets/js/2006.75ebd0c6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2006],{2006:(e,t,n)=>{n.r(t),n.d(t,{default:()=>i});var a=n(2784),l=n(1077),o=n(328),r=n(9991);function i(){return a.createElement(a.Fragment,null,a.createElement(o.d,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(r.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}}}]); \ No newline at end of file diff --git a/docs/assets/js/2043d2f8.82865eed.js b/docs/assets/js/2043d2f8.82865eed.js deleted file mode 100644 index 31e0a0fd..00000000 --- a/docs/assets/js/2043d2f8.82865eed.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5405],{1065:e=>{e.exports=JSON.parse('{"title":"Client-side Javascript","description":"How LiveView client-side Javascript works and how to use it","slug":"/category/client-side-javascript","permalink":"/docs/category/client-side-javascript","navigation":{"previous":{"title":"Example Pub/Sub LiveView","permalink":"/docs/real-time-multi-player-pub-sub/example-pub-sub"},"next":{"title":"Client-side Javascript","permalink":"/docs/client-javascript/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/218c334f.b024827c.js b/docs/assets/js/218c334f.b024827c.js deleted file mode 100644 index b26961f6..00000000 --- a/docs/assets/js/218c334f.b024827c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7203],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var o=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function u(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=o.createContext({}),l=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},c=function(e){var t=l(e.components);return o.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},b=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,c=u(e,["components","mdxType","originalType","parentName"]),b=l(n),m=i,d=b["".concat(s,".").concat(m)]||b[m]||p[m]||r;return n?o.createElement(d,a(a({ref:t},c),{},{components:n})):o.createElement(d,a({ref:t},c))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,a=new Array(r);a[0]=b;var u={};for(var s in t)hasOwnProperty.call(t,s)&&(u[s]=t[s]);u.originalType=e,u.mdxType="string"==typeof e?e:i,a[1]=u;for(var l=2;l<r;l++)a[l]=n[l];return o.createElement.apply(null,a)}return o.createElement.apply(null,n)}b.displayName="MDXCreateElement"},8138:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>p,frontMatter:()=>r,metadata:()=>u,toc:()=>l});var o=n(7896),i=(n(2784),n(876));const r={sidebar_position:9},a="Pub/Sub with `handleInfo`",u={unversionedId:"anatomy-of-a-liveview/handle-info-pub-sub",id:"anatomy-of-a-liveview/handle-info-pub-sub",title:"Pub/Sub with `handleInfo`",description:"Pub/Sub is a common pattern for decoupling processes by allowing messages to be sent to a topic by one process and",source:"@site/docs/03-anatomy-of-a-liveview/handle-info-pub-sub.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/handle-info-pub-sub",permalink:"/docs/anatomy-of-a-liveview/handle-info-pub-sub",draft:!1,tags:[],version:"current",sidebarPosition:9,frontMatter:{sidebar_position:9},sidebar:"tutorialSidebar",previous:{title:"Background Task with `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info-background-task"},next:{title:"LiveViewSocket",permalink:"/docs/category/liveviewsocket"}},s={},l=[{value:"Example Pub/Sub LiveView",id:"example-pubsub-liveview",level:2},{value:"How it works",id:"how-it-works",level:2},{value:"It's that easy!",id:"its-that-easy",level:2}],c={toc:l};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"pubsub-with-handleinfo"},"Pub/Sub with ",(0,i.kt)("inlineCode",{parentName:"h1"},"handleInfo")),(0,i.kt)("p",null,"Pub/Sub is a common pattern for decoupling processes by allowing messages to be sent to a topic by one process and\nreceived asynchronously by another. ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," (and Phoenix LiveView, for that matter) are built on top of Pub/Sub,\nand Pub/Sub is what enables building the real-time, multi-player features with such ease (along with the LiveView\nprogramming model). We will go into more ",(0,i.kt)("a",{parentName:"p",href:"/docs/category/real-time--multi-player"},"detail on Pub/Sub")," in the Real-Time\nMulti-Player docs."),(0,i.kt)("h2",{id:"example-pubsub-liveview"},"Example Pub/Sub LiveView"),(0,i.kt)("p",null,"We're going to extend our counter example to use Pub/Sub, which will make it a real-time, multi-player counter. Here is\nthe code with the Pub/Sub changes highlighted:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="realtimeCounterLiveView.ts" {3-6,14,17-20,27-28,31-32,36-40}',title:'"realtimeCounterLiveView.ts"',"{3-6,14,17-20,27-28,31-32,36-40}":!0},'import { createLiveView, html, SingleProcessPubSub } from "liveviewjs";\n\n// An in-memory count simulating state outside of the LiveView\nlet count = 0;\n// Use a single process pub/sub implementation (for simplicity)\nconst pubSub = new SingleProcessPubSub();\n\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const rtCounterLiveView = createLiveView<\n { count: number }, // Define LiveView Context / State\n { type: "increment" } | { type: "decrement" }, // Define LiveView Events\n { type: "counter"; count: number } // Define LiveView Info messages\n>({\n mount: (socket) => {\n // init state, set count to current count\n socket.assign({ count });\n // subscribe to counter events\n socket.subscribe("counter");\n },\n handleEvent: (event, socket) => {\n // handle increment and decrement events\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n // broadcast the new count\n pubSub.broadcast("counter", { count: count + 1 });\n break;\n case "decrement":\n // broadcast the new count\n pubSub.broadcast("counter", { count: count - 1 });\n break;\n }\n },\n handleInfo: (info, socket) => {\n // receive updates from pubsub and update the context\n count = info.count;\n socket.assign({ count });\n },\n render: async (context) => {\n // render the view based on the state\n const { count } = context;\n return html`\n <div>\n <h1>Count is: ${count}</h1>\n <button phx-click="decrement">-</button>\n <button phx-click="increment">+</button>\n </div>\n `;\n },\n});\n')),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"We're using a ",(0,i.kt)("inlineCode",{parentName:"p"},"SingleProcessPubSub")," implementation for simplicity. In a real application, you would use a\n",(0,i.kt)("inlineCode",{parentName:"p"},"RedisPubSub")," implementation in NodeJS or a ",(0,i.kt)("inlineCode",{parentName:"p"},"BroadcastChannelPubSub")," implementation in for Deno. See the\n",(0,i.kt)("a",{parentName:"p",href:"/docs/real-time-multi-player-pub-sub/overview"},"Pub/Sub docs")," for more details.")),(0,i.kt)("h2",{id:"how-it-works"},"How it works"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"This works just like the ",(0,i.kt)("inlineCode",{parentName:"li"},"counter.ts")," example except we're using Pub/Sub to broadcast the new count to all connected\nclients and subscribe to updates from other clients."),(0,i.kt)("li",{parentName:"ul"},"When a client clicks the increment or decrement button, we broadcast the new count to all connected clients using\n",(0,i.kt)("inlineCode",{parentName:"li"},"pubSub.broadcast"),"."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("strong",{parentName:"li"},"LiveViewJS")," framework automatically routes messages from ",(0,i.kt)("inlineCode",{parentName:"li"},"pubSub.broadcast")," to the ",(0,i.kt)("inlineCode",{parentName:"li"},"handleInfo")," function for\nany LiveView subscribed to the topic."),(0,i.kt)("li",{parentName:"ul"},"In this case, ",(0,i.kt)("inlineCode",{parentName:"li"},"handleInfo")," receives the new count and updates the LiveView context which re-renders the view.")),(0,i.kt)("h2",{id:"its-that-easy"},"It's that easy!"),(0,i.kt)("p",null,"In ~10 lines of code, we've built a real-time, multi-player counter! Sure, that isn't particularly useful, but it shows you how easy it is to create real-time, multi-player applications with very little code and effort."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/26de17c9.0e3042a8.js b/docs/assets/js/26de17c9.0e3042a8.js deleted file mode 100644 index db8bb6f0..00000000 --- a/docs/assets/js/26de17c9.0e3042a8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3476],{876:(e,n,t)=>{t.d(n,{Zo:()=>d,kt:()=>p});var a=t(2784);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?i(Object(t),!0).forEach((function(n){r(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):i(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function s(e,n){if(null==e)return{};var t,a,r=function(e,n){if(null==e)return{};var t,a,r={},i=Object.keys(e);for(a=0;a<i.length;a++)t=i[a],n.indexOf(t)>=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)t=i[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=a.createContext({}),c=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},d=function(e){var n=c(e.components);return a.createElement(l.Provider,{value:n},e.children)},h={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},u=a.forwardRef((function(e,n){var t=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=c(t),p=r,m=u["".concat(l,".").concat(p)]||u[p]||h[p]||i;return t?a.createElement(m,o(o({ref:n},d),{},{components:t})):a.createElement(m,o({ref:n},d))}));function p(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var i=t.length,o=new Array(i);o[0]=u;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var c=2;c<i;c++)o[c]=t[c];return a.createElement.apply(null,o)}return a.createElement.apply(null,t)}u.displayName="MDXCreateElement"},6512:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>i,metadata:()=>s,toc:()=>c});var a=t(7896),r=(t(2784),t(876));const i={sidebar_position:7},o="User-Initiated Event with `handleInfo`",s={unversionedId:"anatomy-of-a-liveview/handle-info-user-initiated",id:"anatomy-of-a-liveview/handle-info-user-initiated",title:"User-Initiated Event with `handleInfo`",description:"Search is a common use case where a user-initiated event might be handled by handleInfo.",source:"@site/docs/03-anatomy-of-a-liveview/handle-info-user-initiated.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/handle-info-user-initiated",permalink:"/docs/anatomy-of-a-liveview/handle-info-user-initiated",draft:!1,tags:[],version:"current",sidebarPosition:7,frontMatter:{sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"LiveView API - `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info"},next:{title:"Background Task with `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info-background-task"}},l={},c=[{value:"Example Search LiveView",id:"example-search-liveview",level:2},{value:"How it works",id:"how-it-works",level:2},{value:"<code>handleInfo</code> Use Cases",id:"handleinfo-use-cases",level:2}],d={toc:c};function h(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,a.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"user-initiated-event-with-handleinfo"},"User-Initiated Event with ",(0,r.kt)("inlineCode",{parentName:"h1"},"handleInfo")),(0,r.kt)("p",null,"Search is a common use case where a user-initiated event might be handled by ",(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo"),"."),(0,r.kt)("h2",{id:"example-search-liveview"},"Example Search LiveView"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="searchLiveView.ts"',title:'"searchLiveView.ts"'},'import { createLiveView, html } from "liveviewjs";\nimport {searchUsers} from "../services/searchUsers";\n/**\n * A basic search that searches for a user by name.\n */\nexport const searchLiveView = createLiveView<\n // Define LiveView Context / State\n { search: string; results: string[], loading: boolean },\n // Define LiveView Events\n { type: "search"; search: string }\n // Define LiveView Infos\n { type: "doSearch"; search: string }\n>({\n // Setup / initialize the LiveView Context (i.e., set search to "" and results to [])\n mount: (socket) => {\n socket.assign({ search: "", results: [] });\n },\n // Handle incoming search events from User input\n handleEvent: (event, socket) => {\n const { search } = socket.context;\n switch (event.type) {\n case "search":\n // set the search data and loading in the context\n socket.assign({ search: event.search, loading: true });\n // Send a doSearch info (event) to the `handleInfo` method\n socket.sendInfo({ type: "doSearch", search: event.search });\n break;\n }\n },\n // Handle incoming info events from the server\n handleInfo: (info, socket) => {\n const { search } = socket.context;\n switch (info.type) {\n case "doSearch":\n // Search for users and update the results in the context\n const results = await searchUsers(info.search);\n socket.assign({ results, loading: false });\n break;\n }\n },\n // Renders the Search View based on the current Context / State\n render: (context) => {\n const { search, results } = context;\n return html`\n <div>\n <h1>Search for a user</h1>\n <input\n type="text"\n placeholder="Search for a user"\n value=${search}\n phx-change="search"\n />\n ${renderResults(results, loading)}\n </div>\n `;\n },\n});\n\nfunction renderResults(results: string[], loading: boolean) {\n if (loading) {\n return html`<div>Loading...</div>`;\n }\n if (results.length === 0) {\n return html`<div>No results</div>`;\n }\n return html`\n <ul>\n ${results.map((result) => html`<li>${result}</li>`)}\n </ul>\n `;\n}\n')),(0,r.kt)("h2",{id:"how-it-works"},"How it works"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"The LiveView renders a form that allows a user to search for a user by name. When the user submits the form, the\n",(0,r.kt)("inlineCode",{parentName:"li"},"handleEvent")," method is called with the ",(0,r.kt)("inlineCode",{parentName:"li"},"search")," event."),(0,r.kt)("li",{parentName:"ul"},"The ",(0,r.kt)("inlineCode",{parentName:"li"},"handleEvent")," method then updates the ",(0,r.kt)("inlineCode",{parentName:"li"},"context")," with the search text, sets ",(0,r.kt)("inlineCode",{parentName:"li"},"loading")," to ",(0,r.kt)("inlineCode",{parentName:"li"},"true"),", and sends a\n",(0,r.kt)("inlineCode",{parentName:"li"},"doSearch")," info event to the ",(0,r.kt)("inlineCode",{parentName:"li"},"handleInfo")," method."),(0,r.kt)("li",{parentName:"ul"},"The ",(0,r.kt)("inlineCode",{parentName:"li"},"handleInfo")," method then performs the search asynchronously (i.e., ",(0,r.kt)("em",{parentName:"li"},"it doesn't block rendering from the\n",(0,r.kt)("inlineCode",{parentName:"em"},"handleEvent")),")."),(0,r.kt)("li",{parentName:"ul"},"When the search is completed ",(0,r.kt)("inlineCode",{parentName:"li"},"handleInfo")," and updates the results in the context and sets ",(0,r.kt)("inlineCode",{parentName:"li"},"loading")," to ",(0,r.kt)("inlineCode",{parentName:"li"},"false"),".\nUpdating the context causes the ",(0,r.kt)("inlineCode",{parentName:"li"},"render")," method to be called again, which renders the search results.")),(0,r.kt)("h2",{id:"handleinfo-use-cases"},(0,r.kt)("inlineCode",{parentName:"h2"},"handleInfo")," Use Cases"),(0,r.kt)("p",null,"There are three main use cases for ",(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo"),":"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Handling an asynchronous process initiated from a user event without blocking the UI"),(0,r.kt)("li",{parentName:"ul"},"Handling an asynchronous process initiated from a background process"),(0,r.kt)("li",{parentName:"ul"},"Handling a pub/sub message")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/284ba78b.71677939.js b/docs/assets/js/284ba78b.71677939.js deleted file mode 100644 index b016e58b..00000000 --- a/docs/assets/js/284ba78b.71677939.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1544],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>p});var a=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,r=function(e,t){if(null==e)return{};var n,a,r={},i=Object.keys(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),d=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=d(e.components);return a.createElement(l.Provider,{value:t},e.children)},h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),u=d(n),p=r,m=u["".concat(l,".").concat(p)]||u[p]||h[p]||i;return n?a.createElement(m,o(o({ref:t},c),{},{components:n})):a.createElement(m,o({ref:t},c))}));function p(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var d=2;d<i;d++)o[d]=n[d];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},5265:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>i,metadata:()=>s,toc:()=>d});var a=n(7896),r=(n(2784),n(876));const i={sidebar_position:8},o="Background Task with `handleInfo`",s={unversionedId:"anatomy-of-a-liveview/handle-info-background-task",id:"anatomy-of-a-liveview/handle-info-background-task",title:"Background Task with `handleInfo`",description:'A "live" dashboard that updates with the latest metrics periodically is another use case that shines with server events',source:"@site/docs/03-anatomy-of-a-liveview/handle-info-background-task.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/handle-info-background-task",permalink:"/docs/anatomy-of-a-liveview/handle-info-background-task",draft:!1,tags:[],version:"current",sidebarPosition:8,frontMatter:{sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"User-Initiated Event with `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info-user-initiated"},next:{title:"Pub/Sub with `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info-pub-sub"}},l={},d=[{value:"Example Live Dashboard LiveView",id:"example-live-dashboard-liveview",level:2},{value:"How it works",id:"how-it-works",level:2},{value:"<code>handleInfo</code> Use Cases",id:"handleinfo-use-cases",level:2}],c={toc:d};function h(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"background-task-with-handleinfo"},"Background Task with ",(0,r.kt)("inlineCode",{parentName:"h1"},"handleInfo")),(0,r.kt)("p",null,'A "live" dashboard that updates with the latest metrics periodically is another use case that shines with server events\nhandled asynchronously by ',(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo"),"."),(0,r.kt)("h2",{id:"example-live-dashboard-liveview"},"Example Live Dashboard LiveView"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="dashboardLiveView.ts"',title:'"dashboardLiveView.ts"'},'/**\n * A dashboard that automatically refreshes every second\n */\nexport const dashboardLiveView = createLiveView<\n // Define LiveView Context / State\n { newOrders: number; salesAmount: number; rating: number },\n // Define LiveView External Events\n {} // No external events\n // Define LiveView Internal Events\n { type: "tick" }\n>({\n mount: (socket) => {\n if (socket.connected) {\n // only start repeating if the socket is connected (i.e., websocket is connected)\n socket.repeat(() => {\n // send the tick event to `handleInfo` every second\n socket.sendInfo({ type: "tick" });\n }, 1000);\n }\n socket.assign(nextRandomData());\n },\n // on tick, update data\n handleInfo: (_, socket) => socket.assign(fetchLatestData()),\n // render the dashboard\n render: (context) => {\n const { newOrders, salesAmount, rating } = context;\n return html`\n <h1>Sales Dashboard</h1>\n <hr />\n <span>\ud83e\udd61 New Orders</span>\n <h2>${newOrders}</h2>\n <hr />\n <span>\ud83d\udcb0 Sales Amount</span>\n <h2>${salesAmount}</h2>\n <hr />\n <span>\ud83c\udf1f Rating</spa>\n <h2>${rating}</h2>\n `;\n },\n});\n')),(0,r.kt)("admonition",{type:"note"},(0,r.kt)("p",{parentName:"admonition"},"The ",(0,r.kt)("inlineCode",{parentName:"p"},"fetchLatestData")," method is not shown here because the implementation is not important. Just assume it\nreturn the latest order, sales, and review data from a database, feed, API, etc.")),(0,r.kt)("h2",{id:"how-it-works"},"How it works"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"The LiveView renders a dashboard that refreshes every second with the latest order, sales, and review data."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"mount")," kicks off the ",(0,r.kt)("inlineCode",{parentName:"li"},"repeat")," function that sends a ",(0,r.kt)("inlineCode",{parentName:"li"},"tick")," event to ",(0,r.kt)("inlineCode",{parentName:"li"},"handleInfo")," every second."),(0,r.kt)("li",{parentName:"ul"},"The ",(0,r.kt)("inlineCode",{parentName:"li"},"handleInfo")," method then fetches the data asynchronously and updates the context with the latest data."),(0,r.kt)("li",{parentName:"ul"},"When the latest data is successfully fetched, the ",(0,r.kt)("inlineCode",{parentName:"li"},"context")," is updated, which causes the ",(0,r.kt)("inlineCode",{parentName:"li"},"render")," method to be called\nagain, pushing the latest data to the client.")),(0,r.kt)("h2",{id:"handleinfo-use-cases"},(0,r.kt)("inlineCode",{parentName:"h2"},"handleInfo")," Use Cases"),(0,r.kt)("p",null,"There are three main use cases for ",(0,r.kt)("inlineCode",{parentName:"p"},"handleInfo"),":"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Handling an asynchronous process initiated from a user event without blocking the UI"),(0,r.kt)("li",{parentName:"ul"},"Handling an asynchronous process initiated from a background process"),(0,r.kt)("li",{parentName:"ul"},"Handling a pub/sub message")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/29143544.a57f485d.js b/docs/assets/js/29143544.a57f485d.js deleted file mode 100644 index 1bd3f0d7..00000000 --- a/docs/assets/js/29143544.a57f485d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6250],{4749:e=>{e.exports=JSON.parse('{"title":"Uploading Files","description":"Magically powerful file uploads with previews, progress, drag & drop, and more \u2728","slug":"/category/uploading-files","permalink":"/docs/category/uploading-files","navigation":{"previous":{"title":"Forms & Changesets Example","permalink":"/docs/forms-and-changesets/use-with-forms"},"next":{"title":"Overview","permalink":"/docs/file-upload/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/2c6e467f.65dfc83b.js b/docs/assets/js/2c6e467f.65dfc83b.js deleted file mode 100644 index 26515e58..00000000 --- a/docs/assets/js/2c6e467f.65dfc83b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6304],{8530:e=>{e.exports=JSON.parse('{"title":"Real-time / Multi-player","description":"Easy real-time, multi-player support with LiveViewJS Pub/Sub","slug":"/category/real-time--multi-player","permalink":"/docs/category/real-time--multi-player","navigation":{"previous":{"title":"Upload Config Options","permalink":"/docs/file-upload/upload-config-options"},"next":{"title":"Overview","permalink":"/docs/real-time-multi-player-pub-sub/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/2f79913e.df91907d.js b/docs/assets/js/2f79913e.df91907d.js deleted file mode 100644 index a873abe6..00000000 --- a/docs/assets/js/2f79913e.df91907d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[728],{1432:e=>{e.exports=JSON.parse('{"title":"Webserver Integrations","description":"How LiveViewJS integrates with webservers and how to add support for other webservers","slug":"/category/webserver-integrations","permalink":"/docs/category/webserver-integrations","navigation":{"previous":{"title":"Push Command","permalink":"/docs/js-commands/push-cmd"},"next":{"title":"Webserver Integration","permalink":"/docs/webserver-integration/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/3081b21a.ce19d10e.js b/docs/assets/js/3081b21a.ce19d10e.js deleted file mode 100644 index 544abecc..00000000 --- a/docs/assets/js/3081b21a.ce19d10e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9944],{876:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var o=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,o,r=function(e,t){if(null==e)return{};var n,o,r={},i=Object.keys(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o<i.length;o++)n=i[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=o.createContext({}),s=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=s(e.components);return o.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=s(n),f=r,m=d["".concat(p,".").concat(f)]||d[f]||c[f]||i;return n?o.createElement(m,a(a({ref:t},u),{},{components:n})):o.createElement(m,a({ref:t},u))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:r,a[1]=l;for(var s=2;s<i;s++)a[s]=n[s];return o.createElement.apply(null,a)}return o.createElement.apply(null,n)}d.displayName="MDXCreateElement"},1067:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var o=n(7896),r=(n(2784),n(876));const i={sidebar_position:4},a="Upload Config Options",l={unversionedId:"file-upload/upload-config-options",id:"file-upload/upload-config-options",title:"Upload Config Options",description:"These are the options you can pass into the allowUpload method to configure the upload.",source:"@site/docs/08-file-upload/upload-config-options.md",sourceDirName:"08-file-upload",slug:"/file-upload/upload-config-options",permalink:"/docs/file-upload/upload-config-options",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Built-in Drag and Drop",permalink:"/docs/file-upload/drag-and-drop"},next:{title:"Real-time / Multi-player",permalink:"/docs/category/real-time--multi-player"}},p={},s=[],u={toc:s};function c(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,o.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"upload-config-options"},"Upload Config Options"),(0,r.kt)("p",null,"These are the options you can pass into the ",(0,r.kt)("inlineCode",{parentName:"p"},"allowUpload")," method to configure the upload."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"accept"),": an array of strings that represent the file extensions and/or mime types that are allowed to be uploaded.\nFor example, ",(0,r.kt)("inlineCode",{parentName:"li"},'[".png", ".jpg", ".jpeg", ".gif"]')," will only allow images to be uploaded. See\n",(0,r.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers"},"unique file type specifiers"),"\nfor more information. Defaults to ",(0,r.kt)("inlineCode",{parentName:"li"},"[]")," (no restrictions)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"maxEntries"),": the maximum number of files that can be uploaded. If the user tries to upload more than this number of\nfiles, an error will be present in the upload config. Defaults to ",(0,r.kt)("inlineCode",{parentName:"li"},"1"),"."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"maxFileSize"),": the maximum file size (in bytes) that can be uploaded. If the user tries to upload a file that is\nlarger than this number, an error will be present in the upload config. Defaults to ",(0,r.kt)("inlineCode",{parentName:"li"},"10 * 1024 * 1024")," (10MB)."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"autoUpload"),": if ",(0,r.kt)("inlineCode",{parentName:"li"},"true"),", the file will be uploaded as soon as it is selected by the user. If ",(0,r.kt)("inlineCode",{parentName:"li"},"false"),", the file will\nnot be uploaded until the user initiates the form's save event. The default is ",(0,r.kt)("inlineCode",{parentName:"li"},"false"),".")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/30a24c52.1683ba64.js b/docs/assets/js/30a24c52.1683ba64.js deleted file mode 100644 index a82b1c24..00000000 --- a/docs/assets/js/30a24c52.1683ba64.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[453],{8605:e=>{e.exports=JSON.parse('{"label":"hello","permalink":"/blog/tags/hello","allTagsPath":"/blog/tags","count":2}')}}]); \ No newline at end of file diff --git a/docs/assets/js/33cd0f01.e15f00ce.js b/docs/assets/js/33cd0f01.e15f00ce.js deleted file mode 100644 index cda352d7..00000000 --- a/docs/assets/js/33cd0f01.e15f00ce.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[458],{876:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(2784);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(r),m=a,h=d["".concat(l,".").concat(m)]||d[m]||c[m]||o;return r?n.createElement(h,i(i({ref:t},u),{},{components:r})):n.createElement(h,i({ref:t},u))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,i[1]=s;for(var p=2;p<o;p++)i[p]=r[p];return n.createElement.apply(null,i)}return n.createElement.apply(null,r)}d.displayName="MDXCreateElement"},3254:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var n=r(7896),a=(r(2784),r(876));const o={sidebar_position:2},i="NodeJS - Run the Examples",s={unversionedId:"quick-starts/nodejs-run-examples",id:"quick-starts/nodejs-run-examples",title:"NodeJS - Run the Examples",description:"LiveViewJS ships with over a dozen example LiveViews that show everything from simple button-based events to",source:"@site/docs/02-quick-starts/nodejs-run-examples.md",sourceDirName:"02-quick-starts",slug:"/quick-starts/nodejs-run-examples",permalink:"/docs/quick-starts/nodejs-run-examples",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Download the Repo",permalink:"/docs/quick-starts/get-liveviewjs-repo"},next:{title:"NodeJS - Build a LiveView",permalink:"/docs/quick-starts/nodejs-build-first-liveview"}},l={},p=[{value:"Prerequisite",id:"prerequisite",level:2},{value:"Run the Examples",id:"run-the-examples",level:2},{value:"Explore the Examples",id:"explore-the-examples",level:2}],u={toc:p};function c(e){let{components:t,...o}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"nodejs---run-the-examples"},"NodeJS - Run the Examples"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," ships with over a dozen example LiveViews that show everything from simple button-based events to\nreal-time, multi-player views. It takes approximately \u23f1 1 minute to get these examples up and running and is a good way\nto get a feel for the user experience of a LiveView. Let's get started!"),(0,a.kt)("h2",{id:"prerequisite"},"Prerequisite"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://nodejs.org/en/download/"},"Node.js")," version 18.x or above."),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"We rely on the NodeJS Fetch API, which is only available in 18+.")),(0,a.kt)("p",null,"If you haven't already, ",(0,a.kt)("a",{parentName:"p",href:"get-liveviewjs-repo"},"download the ",(0,a.kt)("strong",{parentName:"a"},"LiveViewJS")," repo"),"."),(0,a.kt)("h2",{id:"run-the-examples"},"Run the Examples"),(0,a.kt)("p",null,"First, load the NPM dependencies:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"# install the NPM dependencies\nnpm install\n")),(0,a.kt)("p",null,"Then, start the express server with the examples:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"# run the examples\nnpm run start -w packages/express\n")),(0,a.kt)("p",null,"Point your browser to ",(0,a.kt)("a",{parentName:"p",href:"http://localhost:4001"},"http://localhost:4001")),(0,a.kt)("h2",{id:"explore-the-examples"},"Explore the Examples"),(0,a.kt)("p",null,"You should see something like the screenshot below including a list of examples with a brief description, a link to the\nrunning LiveView, and a link to the source code for each example."),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"LiveViewJS Examples Screenshot",src:r(7381).Z,width:"1552",height:"1382"})))}c.isMDXComponent=!0},7381:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/liveviewjs_examples_rec-2cc096a7ff8f675e44f0a7fa0c1108c7.gif"}}]); \ No newline at end of file diff --git a/docs/assets/js/355cffd0.93571283.js b/docs/assets/js/355cffd0.93571283.js deleted file mode 100644 index c04ccf6a..00000000 --- a/docs/assets/js/355cffd0.93571283.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3380],{876:(e,t,a)=>{a.d(t,{Zo:()=>d,kt:()=>m});var n=a(2784);function i(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?o(Object(a),!0).forEach((function(t){i(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):o(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function s(e,t){if(null==e)return{};var a,n,i=function(e,t){if(null==e)return{};var a,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},d=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},h={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),c=p(a),m=i,u=c["".concat(l,".").concat(m)]||c[m]||h[m]||o;return a?n.createElement(u,r(r({ref:t},d),{},{components:a})):n.createElement(u,r({ref:t},d))}));function m(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var p=2;p<o;p++)r[p]=a[p];return n.createElement.apply(null,r)}return n.createElement.apply(null,a)}c.displayName="MDXCreateElement"},1200:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var n=a(7896),i=(a(2784),a(876));const o={sidebar_position:2},r="Changesets",s={unversionedId:"forms-and-changesets/changesets",id:"forms-and-changesets/changesets",title:"Changesets",description:"High Level",source:"@site/docs/07-forms-and-changesets/changesets.md",sourceDirName:"07-forms-and-changesets",slug:"/forms-and-changesets/changesets",permalink:"/docs/forms-and-changesets/changesets",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Overview",permalink:"/docs/forms-and-changesets/overview"},next:{title:"Forms & Changesets Example",permalink:"/docs/forms-and-changesets/use-with-forms"}},l={},p=[{value:"High Level",id:"high-level",level:2},{value:"Create a Zod Schema",id:"create-a-zod-schema",level:2},{value:"Infer the Type based on the Schema",id:"infer-the-type-based-on-the-schema",level:2},{value:"Generate a LiveViewChangesetFactory",id:"generate-a-liveviewchangesetfactory",level:2},{value:"Using a LiveViewChangesetFactory",id:"using-a-liveviewchangesetfactory",level:2},{value:"Use a LiveViewChangesetFactory to create a LiveViewChangeset",id:"use-a-liveviewchangesetfactory-to-create-a-liveviewchangeset",level:2},{value:"Invalid Data",id:"invalid-data",level:2},{value:"What about the action string?",id:"what-about-the-action-string",level:2},{value:"Next Steps",id:"next-steps",level:2}],d={toc:p};function h(e){let{components:t,...a}=e;return(0,i.kt)("wrapper",(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"changesets"},"Changesets"),(0,i.kt)("h2",{id:"high-level"},"High Level"),(0,i.kt)("p",null,"At a high level, Changesets are used to track creation and mutation of data models in LiveView projects and synchronize\nthem with HTML Forms and the user input that drives them."),(0,i.kt)("p",null,"Changeset are built on top of ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/colinhacks/zod"},"Zod"),' which is a "Typescript-first schema validation\nwith static type inference" library.'),(0,i.kt)("p",null,"Let's go through the steps of creating a changeset helper for a Book data model."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"The ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/colinhacks/zod#basic-usage"},"basics of Zod")," are pretty easy to pick up especially if you\nare familiar with Typescript. Even if you are not too familiar with Zod or Typescript, the concept is Zod is pretty\nstraight forward. Essentially you are defining a schema and then parsing the input data against the schema. If the input\ndata matches the schema then the data is valid. If the input data does not match the schema then the data is invalid and\nyou can use the error messages to display validation errors to the user.")),(0,i.kt)("h2",{id:"create-a-zod-schema"},"Create a Zod Schema"),(0,i.kt)("p",null,"First, we need to define a Zod schema for the data model:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import { z } from "zod";\n\n// Use Zod to define the schema for the Book model\nconst BookSchema = z.object({\n id: z.string().default(nanoid),\n name: z.string().min(2).max(100),\n author: z.string().min(2).max(100),\n checked_out: z.boolean().default(false),\n});\n')),(0,i.kt)("p",null,"As you can see the ",(0,i.kt)("inlineCode",{parentName:"p"},"BookSchema")," has an ",(0,i.kt)("inlineCode",{parentName:"p"},"id"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"genre")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"checked_out")," property. The ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," is a string and is\ngenerated using the ",(0,i.kt)("inlineCode",{parentName:"p"},"nanoid")," function by default. The ",(0,i.kt)("inlineCode",{parentName:"p"},"name")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"author")," are strings and must be at least 2 characters\nlong and no more than 100 characters long. The ",(0,i.kt)("inlineCode",{parentName:"p"},"checked_out")," property is a boolean and is set to ",(0,i.kt)("inlineCode",{parentName:"p"},"false")," by default."),(0,i.kt)("h2",{id:"infer-the-type-based-on-the-schema"},"Infer the Type based on the Schema"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// infer the Book type from the schema\ntype Book = z.infer<typeof BookSchema>;\n")),(0,i.kt)("p",null,"Now, we have a schema and a type for the Book data model. The ",(0,i.kt)("inlineCode",{parentName:"p"},"z.infer")," function is used to infer a valid type from the\nschema."),(0,i.kt)("p",null,"So far this is basic Zod usage. Now that we have the schema and type we can use them to create a\n",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory"),"."),(0,i.kt)("h2",{id:"generate-a-liveviewchangesetfactory"},"Generate a LiveViewChangesetFactory"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import { newChangesetFactory } from "liveviewjs";\n\n// generate changeset factory\nconst bookCSF = newChangesetFactory<Book>(BookSchema);\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"newChangesetFactory")," function takes a Zod schema (and type annotation) and returns a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," for\nthat schema and type. The ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," is used to create ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset"),"s for the ",(0,i.kt)("inlineCode",{parentName:"p"},"Book")," model."),(0,i.kt)("h2",{id:"using-a-liveviewchangesetfactory"},"Using a LiveViewChangesetFactory"),(0,i.kt)("p",null,"Despite it's long name, a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," is simply a function that takes two ",(0,i.kt)("inlineCode",{parentName:"p"},"Partial"),"s and an (optional)\naction string and returns a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset"),". The first ",(0,i.kt)("inlineCode",{parentName:"p"},"Partial")," is the starting data model and the second ",(0,i.kt)("inlineCode",{parentName:"p"},"Partial"),"\nis the changes to the data model."),(0,i.kt)("p",null,"Here is the signature of the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * A factory for creating a changeset for a given existing data model, updated data model, and optional action.\n */\nexport type LiveViewChangesetFactory<T> = (\n existing: Partial<T>,\n newAttrs: Partial<T>,\n action?: string\n) => LiveViewChangeset<T>;\n")),(0,i.kt)("p",null,"When you generate a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," for a given schema and type, the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory"),' is created\n(and "typed") for that schema and type. So, if you generate a ',(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," for a ",(0,i.kt)("inlineCode",{parentName:"p"},"Book")," schema and type,\nthen the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," will be typed to a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for a ",(0,i.kt)("inlineCode",{parentName:"p"},"Book")," model and will only accept\n",(0,i.kt)("inlineCode",{parentName:"p"},"Partial<Book>"),"s as the first and second arguments."),(0,i.kt)("h2",{id:"use-a-liveviewchangesetfactory-to-create-a-liveviewchangeset"},"Use a LiveViewChangesetFactory to create a LiveViewChangeset"),(0,i.kt)("p",null,"Now that we have a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," for the ",(0,i.kt)("inlineCode",{parentName:"p"},"Book")," model, we can use it to create a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for a\n",(0,i.kt)("inlineCode",{parentName:"p"},"Book")," data."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'const bookData: Partial<Book> = {\n name: "The Hobbit",\n author: "J.R.R. Tolkien",\n};\n// create a new changeset for a new book\nconst bookCS = changeset({}, bookData, "create");\n')),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"bookCS")," object is a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for a ",(0,i.kt)("inlineCode",{parentName:"p"},"Book")," model. Zod parsed and validated the ",(0,i.kt)("inlineCode",{parentName:"p"},"bookData")," we passed in."),(0,i.kt)("p",null,"A ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," has the following properties:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"valid: boolean")," - does the resulting merged data model pass all validation rules?"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"changes: Partial<T>")," - just the parts of the model that have been changed relative to the existing model"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"data: T")," - the data model after the changeset has been applied (which may be invalid or valid depending on the\nvalidations)"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"errors: { [Property in keyof T]?: string | undefined; }")," - an object that maps property names to error messages if\nthe changeset has invalid data"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"action?: string")," - the changeset action (or undefined if unset)")),(0,i.kt)("p",null,"So now that we've created a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for the book data we can ask it if the data is valid or not:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"bookCS.valid; // => true\n")),(0,i.kt)("p",null,"Then we can ask it for the new data:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'bookCS.data; // => { id: "some-random-id", name: "The Hobbit", author: "J.R.R. Tolkien", checked_out: false }\n')),(0,i.kt)("h2",{id:"invalid-data"},"Invalid Data"),(0,i.kt)("p",null,"Let's create a new ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for a book with invalid data:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'const invalidBookData: Partial<Book> = {\n name: "a",\n author: "b",\n};\n// create a new changeset for this book\nconst invalidBookCS = changeset({}, invalidBookData, "create");\n')),(0,i.kt)("p",null,"Now, we can ask the ",(0,i.kt)("inlineCode",{parentName:"p"},"invalidBookCS")," if it is valid:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"invalidBookCS.valid; // => false\n")),(0,i.kt)("p",null,"And we can ask it for the errors:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'invalidBookCS.errors; // => { name: "Must be at least 2 characters", author: "Must be at least 2 characters" }\n')),(0,i.kt)("h2",{id:"what-about-the-action-string"},"What about the action string?"),(0,i.kt)("p",null,"The value of the action string has no significance in an of itself. The presence of the action string however does\nimpact the ",(0,i.kt)("inlineCode",{parentName:"p"},"valid")," property of the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," returned by the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory"),". ",(0,i.kt)("strong",{parentName:"p"},"If the action\nstring is NOT set")," the ",(0,i.kt)("inlineCode",{parentName:"p"},"valid")," property will always return ",(0,i.kt)("inlineCode",{parentName:"p"},"true"),". ",(0,i.kt)("strong",{parentName:"p"},"If the action string IS set")," the ",(0,i.kt)("inlineCode",{parentName:"p"},"valid")," property\nwill return ",(0,i.kt)("inlineCode",{parentName:"p"},"true")," if the data is valid and ",(0,i.kt)("inlineCode",{parentName:"p"},"false")," if the data is invalid."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},'We pass "empty" (i.e., no action string) changesets to form helpers in the ',(0,i.kt)("inlineCode",{parentName:"p"},"render"),' function otherwise there\nwould be errors on the form when the page is first loaded. "Empty" changesets are always valid.')),(0,i.kt)("h2",{id:"next-steps"},"Next Steps"),(0,i.kt)("p",null,'Now that you understand "Changesets", we can show you how they are powerful partners with the Form Events we discussed\nearlier.'))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/3de70efe.6513b0c9.js b/docs/assets/js/3de70efe.6513b0c9.js deleted file mode 100644 index 7ccfc56e..00000000 --- a/docs/assets/js/3de70efe.6513b0c9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1247],{876:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>h});var n=r(2784);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){o(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function l(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=n.createContext({}),p=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},d=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(r),h=o,v=c["".concat(s,".").concat(h)]||c[h]||u[h]||i;return r?n.createElement(v,a(a({ref:t},d),{},{components:r})):n.createElement(v,a({ref:t},d))}));function h(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var p=2;p<i;p++)a[p]=r[p];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}c.displayName="MDXCreateElement"},9962:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var n=r(7896),o=(r(2784),r(876));const i={sidebar_position:1},a="Download the Repo",l={unversionedId:"quick-starts/get-liveviewjs-repo",id:"quick-starts/get-liveviewjs-repo",title:"Download the Repo",description:"The fastest way to run the example or build your own LiveView is by downloading the LiveViewJS repo. This repo",source:"@site/docs/02-quick-starts/get-liveviewjs-repo.md",sourceDirName:"02-quick-starts",slug:"/quick-starts/get-liveviewjs-repo",permalink:"/docs/quick-starts/get-liveviewjs-repo",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Quick Starts",permalink:"/docs/category/quick-starts"},next:{title:"NodeJS - Run the Examples",permalink:"/docs/quick-starts/nodejs-run-examples"}},s={},p=[{value:"Get the Code",id:"get-the-code",level:2},{value:"Clone the <strong>LiveViewJS</strong> GitHub repository:",id:"clone-the-liveviewjs-github-repository",level:3},{value:"OR fetch with <code>degit</code>:",id:"or-fetch-with-degit",level:3},{value:"Change to the LiveViewJS Directory",id:"change-to-the-liveviewjs-directory",level:2},{value:"Node or Deno?",id:"node-or-deno",level:2},{value:"Node",id:"node",level:3},{value:"Deno",id:"deno",level:3}],d={toc:p};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"download-the-repo"},"Download the Repo"),(0,o.kt)("p",null,"The fastest way to run the example or build your own LiveView is by downloading the ",(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," repo. This repo\ncontains all the examples and configured webserver code for Express (NodeJS) and Oak (Deno)."),(0,o.kt)("h2",{id:"get-the-code"},"Get the Code"),(0,o.kt)("p",null,"Either use ",(0,o.kt)("inlineCode",{parentName:"p"},"git clone")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"degit")," to get the ",(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," GitHub repository."),(0,o.kt)("admonition",{type:"info"},(0,o.kt)("p",{parentName:"admonition"},(0,o.kt)("inlineCode",{parentName:"p"},"degit")," is a lightweight way to clone a repo without the .git parts.\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/Rich-Harris/degit"},"More info"),".")),(0,o.kt)("h3",{id:"clone-the-liveviewjs-github-repository"},"Clone the ",(0,o.kt)("strong",{parentName:"h3"},"LiveViewJS")," GitHub repository:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# clone the LiveViewJS repo\ngit clone https://github.com/floodfx/liveviewjs.git\n")),(0,o.kt)("h3",{id:"or-fetch-with-degit"},"OR fetch with ",(0,o.kt)("inlineCode",{parentName:"h3"},"degit"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# copy the LiveViewJS repo\nnpx degit floodfx/liveviewjs liveviewjs\n")),(0,o.kt)("h2",{id:"change-to-the-liveviewjs-directory"},"Change to the LiveViewJS Directory"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"# cd to the LiveViewJS directory\ncd liveviewjs\n")),(0,o.kt)("h2",{id:"node-or-deno"},"Node or Deno?"),(0,o.kt)("p",null,(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," runs on both Node and Deno, but you'll probably want to start down one path or the other depending on what\nruntime you are more familiar with or are already using."),(0,o.kt)("admonition",{type:"note"},(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," library APIs are the same so you can build your LiveViews on one platform and run them on the\nother unless you are using Deno or Node-specific APIs in your LiveView implementation.")),(0,o.kt)("h3",{id:"node"},"Node"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"nodejs-run-examples"},"NodeJS - Run the Examples")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"nodejs-build-first-liveview"},"NodeJS - Build your First LiveView"))),(0,o.kt)("h3",{id:"deno"},"Deno"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"deno-run-examples"},"Deno - Run the Examples")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"deno-build-first-liveview"},"Deno - Build your First LiveView"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/3fc56706.15e86c2f.js b/docs/assets/js/3fc56706.15e86c2f.js deleted file mode 100644 index cb907fbd..00000000 --- a/docs/assets/js/3fc56706.15e86c2f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[4085],{876:(e,r,t)=>{t.d(r,{Zo:()=>l,kt:()=>f});var n=t(2784);function o(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function a(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function i(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?a(Object(t),!0).forEach((function(r){o(e,r,t[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):a(Object(t)).forEach((function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))}))}return e}function c(e,r){if(null==e)return{};var t,n,o=function(e,r){if(null==e)return{};var t,n,o={},a=Object.keys(e);for(n=0;n<a.length;n++)t=a[n],r.indexOf(t)>=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n<a.length;n++)t=a[n],r.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var s=n.createContext({}),p=function(e){var r=n.useContext(s),t=r;return e&&(t="function"==typeof e?e(r):i(i({},r),e)),t},l=function(e){var r=p(e.components);return n.createElement(s.Provider,{value:r},e.children)},u={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},d=n.forwardRef((function(e,r){var t=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),d=p(t),f=o,m=d["".concat(s,".").concat(f)]||d[f]||u[f]||a;return t?n.createElement(m,i(i({ref:r},l),{},{components:t})):n.createElement(m,i({ref:r},l))}));function f(e,r){var t=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=d;var c={};for(var s in r)hasOwnProperty.call(r,s)&&(c[s]=r[s]);c.originalType=e,c.mdxType="string"==typeof e?e:o,i[1]=c;for(var p=2;p<a;p++)i[p]=t[p];return n.createElement.apply(null,i)}return n.createElement.apply(null,t)}d.displayName="MDXCreateElement"},1080:(e,r,t)=>{t.r(r),t.d(r,{assets:()=>s,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>c,toc:()=>p});var n=t(7896),o=(t(2784),t(876));const a={sidebar_position:5},i="Root and Page Renderers",c={unversionedId:"misc/root-and-page-renderers",id:"misc/root-and-page-renderers",title:"Root and Page Renderers",description:"",source:"@site/docs/13-misc/root-and-page-renderers.md",sourceDirName:"13-misc",slug:"/misc/root-and-page-renderers",permalink:"/docs/misc/root-and-page-renderers",draft:!1,tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"Debugging LiveViews",permalink:"/docs/misc/debugging-wire"},next:{title:"Flash Messages",permalink:"/docs/misc/flash"}},s={},p=[],l={toc:p};function u(e){let{components:r,...t}=e;return(0,o.kt)("wrapper",(0,n.Z)({},l,t,{components:r,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"root-and-page-renderers"},"Root and Page Renderers"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/42132283.343e6bdb.js b/docs/assets/js/42132283.343e6bdb.js deleted file mode 100644 index 0cc3e823..00000000 --- a/docs/assets/js/42132283.343e6bdb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3369],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var o=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=o.createContext({}),c=function(e){var t=o.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=c(e.components);return o.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),h=i,m=u["".concat(l,".").concat(h)]||u[h]||d[h]||r;return n?o.createElement(m,a(a({ref:t},p),{},{components:n})):o.createElement(m,a({ref:t},p))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,a=new Array(r);a[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var c=2;c<r;c++)a[c]=n[c];return o.createElement.apply(null,a)}return o.createElement.apply(null,n)}u.displayName="MDXCreateElement"},8281:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var o=n(7896),i=(n(2784),n(876));const r={sidebar_position:1},a="Client-side Javascript",s={unversionedId:"client-javascript/overview",id:"client-javascript/overview",title:"Client-side Javascript",description:"LiveViewJS pages do, in fact, require some client-side javascript to be loaded as part of the HTML page. This",source:"@site/docs/10-client-javascript/overview.md",sourceDirName:"10-client-javascript",slug:"/client-javascript/overview",permalink:"/docs/client-javascript/overview",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Client-side Javascript",permalink:"/docs/category/client-side-javascript"},next:{title:'"Hooks" (not the React kind)',permalink:"/docs/client-javascript/client-hooks"}},l={},c=[{value:"Default client-side JS:",id:"default-client-side-js",level:2},{value:"Walkthrough of the client-side JS",id:"walkthrough-of-the-client-side-js",level:2},{value:"Imports",id:"imports",level:3},{value:"LiveView Websocket Route",id:"liveview-websocket-route",level:3},{value:"CSRF Token",id:"csrf-token",level:3},{value:"Create the LiveSocket",id:"create-the-livesocket",level:3},{value:"Progress Bar",id:"progress-bar",level:3},{value:"Connect to the LiveSocket",id:"connect-to-the-livesocket",level:3},{value:"Debugging and Latency Simulation",id:"debugging-and-latency-simulation",level:3},{value:"Default client-side JS is good start",id:"default-client-side-js-is-good-start",level:3}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"client-side-javascript"},"Client-side Javascript"),(0,i.kt)("p",null,"LiveViewJS pages do, in fact, require some client-side javascript to be loaded as part of the HTML page. This\nclient-side javascript handles parsing the ",(0,i.kt)("inlineCode",{parentName:"p"},"phx-*")," attributes, connecting to the server (via websocket), appling the\ndiffs, and turning user interactions into events, among other things."),(0,i.kt)("p",null,"Let's look at the client-side javascript in more detail."),(0,i.kt)("h2",{id:"default-client-side-js"},"Default client-side JS:"),(0,i.kt)("p",null,"The default Typescript that is compiled into the client-side javascript loaded by ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," is the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import NProgress from "nprogress";\nimport { Socket } from "phoenix";\nimport "phoenix_html";\nimport { LiveSocket } from "phoenix_live_view";\n\n// Define the route that websockets will use to connect to your server\nconst url = "/live";\n\n// Pull out the csrf token from the meta tag\nlet csrfToken = document.querySelector("meta[name=\'csrf-token\']").getAttribute("content");\n\n// Create the LiveSocket\nlet liveSocket = new LiveSocket(url, Socket, { params: { _csrf_token: csrfToken } });\n\n// Show progress bar on live navigation and form submits (requires NProgress css)\nwindow.addEventListener("phx:page-loading-start", (info) => NProgress.start());\nwindow.addEventListener("phx:page-loading-stop", (info) => NProgress.done());\n\n// connect if there are any LiveViews on the page\nliveSocket.connect();\n\n// If you want to expose liveSocket messages in the console for debugging, uncomment the following:\n// liveSocket.enableDebug();\n\n// If you want to simulate request latency, you can uncomment the following\n// liveSocket.enableLatencySim(1000)\n\n// finally add the liveSocket to the window\n(window as any).liveSocket = liveSocket;\n')),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"The libraries used in ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," are exactly the same libraries used in Phoenix LiveView. Beyond ensuring we\ndon't reinvent the wheel, this provides ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," with battle-tested, robust code that has proven extremely\nreliable.")),(0,i.kt)("h2",{id:"walkthrough-of-the-client-side-js"},"Walkthrough of the client-side JS"),(0,i.kt)("p",null,"Let's walk through the client-side JS in more detail."),(0,i.kt)("h3",{id:"imports"},"Imports"),(0,i.kt)("p",null,"First, we are importing the ",(0,i.kt)("inlineCode",{parentName:"p"},"NProgress")," library which is used to show a progress bar when the page is loading. We are\nalso importing the ",(0,i.kt)("inlineCode",{parentName:"p"},"Socket")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveSocket")," classes from the ",(0,i.kt)("inlineCode",{parentName:"p"},"phoenix")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"phoenix_live_view")," libraries along with the\n",(0,i.kt)("inlineCode",{parentName:"p"},"phoenix_html")," library."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import NProgress from "nprogress";\nimport { Socket } from "phoenix";\nimport "phoenix_html";\nimport { LiveSocket } from "phoenix_live_view";\n...\n')),(0,i.kt)("h3",{id:"liveview-websocket-route"},"LiveView Websocket Route"),(0,i.kt)("p",null,"Next, we define the route that websockets will use to connect to your server. All websockets will be routed through this\nURL. From there, ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," uses the page URL to determine which LiveView to route the connection to. If you change\nthis URL, you will need to change the server-side code that handles the websocket connection."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'...\n// Define the route that websockets will use to connect to your server\nconst url = "/live";\n...\n')),(0,i.kt)("h3",{id:"csrf-token"},"CSRF Token"),(0,i.kt)("p",null,"Next, we pull out the CSRF token from the meta tag. This is used to authenticate the websocket connection."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'...\n// Pull out the csrf token from the meta tag\nlet csrfToken = document.querySelector("meta[name=\'csrf-token\']").getAttribute("content");\n...\n')),(0,i.kt)("h3",{id:"create-the-livesocket"},"Create the LiveSocket"),(0,i.kt)("p",null,"Then, we create the LiveSocket. This is the object that handles the websocket connection and all the LiveView logic. We\npass in the ",(0,i.kt)("inlineCode",{parentName:"p"},"url"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"Socket"),", and any other options we want to configure the LiveSocket with."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"...\n// Create the LiveSocket\nlet liveSocket = new LiveSocket(url, Socket, { params: { _csrf_token: csrfToken } });\n...\n")),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"The options that we can pass into the LiveSocket are:"),(0,i.kt)("ul",{parentName:"admonition"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"bindingPrefix"),' - the prefix to use for phoenix bindings. Defaults "phx-"'),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"params")," - the connect_params to pass to the view's mount callback. May be a literal object or closure returning an\nobject. When a closure is provided, the function receives the view's element."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"hooks")," \u2013 a reference to a user-defined hooks namespace, containing client callbacks for server/client interop. (We'll\ncover Hooks in a later section)"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"uploaders")," \u2013 a reference to a user-defined uploaders namespace, containing client callbacks for client-side\ndirect-to-cloud uploads. (LiveViewJS currently does not support user-defined uploaders but we plan to add this in the\nfuture)"))),(0,i.kt)("h3",{id:"progress-bar"},"Progress Bar"),(0,i.kt)("p",null,"Next, we add event listeners to show a progress bar when the page is loading. This requires the ",(0,i.kt)("inlineCode",{parentName:"p"},"NProgress")," library and\nthe ",(0,i.kt)("inlineCode",{parentName:"p"},"NProgress")," css."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'...\n// Show progress bar on live navigation and form submits (requires NProgress css)\nwindow.addEventListener("phx:page-loading-start", (info) => NProgress.start());\nwindow.addEventListener("phx:page-loading-stop", (info) => NProgress.done());\n...\n')),(0,i.kt)("p",null,"You could swap NPorgress out for any other progress bar library you want like\n",(0,i.kt)("a",{parentName:"p",href:"http://buunguyen.github.io/topbar/"},"topbar"),". If you did that you would have to update the event listeners to match the\nevents that your progress bar library uses."),(0,i.kt)("h3",{id:"connect-to-the-livesocket"},"Connect to the LiveSocket"),(0,i.kt)("p",null,"Lastly, we connect to the LiveSocket. This will connect to the websocket and start the LiveView process."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"...\n// connect if there are any LiveViews on the page\nliveSocket.connect();\n...\n// add the liveSocket to the window\n(window as any).liveSocket = liveSocket;\n")),(0,i.kt)("h3",{id:"debugging-and-latency-simulation"},"Debugging and Latency Simulation"),(0,i.kt)("p",null,"There are a couple of lines that are commented out by default. If you want to expose liveSocket messages in the console\nfor debugging or turn on latency simulation, you can uncomment the following lines:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"...\n// liveSocket.enableDebug();\n// liveSocket.enableLatencySim(1000)\n...\n")),(0,i.kt)("h3",{id:"default-client-side-js-is-good-start"},"Default client-side JS is good start"),(0,i.kt)("p",null,"For the most part, you shouldn't need to change the client-side JS especially at first. As you build more LiveViews you\nmight run into a need to do some more client-side DOM manipulation or handle events pushed from the server. In these\ncases, you may need to add what is called a \"Hook\". We'll cover Hooks in the next section."),(0,i.kt)("admonition",{title:'LiveView "Hooks" are a completely different concept from "Hooks" in React. LiveView Hooks are a way to add',type:"caution"},(0,i.kt)("p",{parentName:"admonition"},"custom client-side logic to your LiveView. Unfortunately, sometimes naming conflicts like this happen. Just remember\nthat LiveView Hooks are not the same as React Hooks. :::")))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/42d1d491.cf07e823.js b/docs/assets/js/42d1d491.cf07e823.js deleted file mode 100644 index 66983b9d..00000000 --- a/docs/assets/js/42d1d491.cf07e823.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3219],{4626:e=>{e.exports=JSON.parse('{"title":"Anatomy of a LiveView","description":"Deep dive into the LiveView API and lifecycle functions","slug":"/category/anatomy-of-a-liveview","permalink":"/docs/category/anatomy-of-a-liveview","navigation":{"previous":{"title":"Deno - Build a LiveView","permalink":"/docs/quick-starts/deno-build-first-liveview"},"next":{"title":"LiveView API","permalink":"/docs/anatomy-of-a-liveview/liveview-api"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/4c9e35b1.7fb6c67c.js b/docs/assets/js/4c9e35b1.7fb6c67c.js deleted file mode 100644 index 91e546d2..00000000 --- a/docs/assets/js/4c9e35b1.7fb6c67c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9035],{499:e=>{e.exports=JSON.parse('{"permalink":"/blog/tags/hola","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/4ec89f2d.d296e41b.js b/docs/assets/js/4ec89f2d.d296e41b.js deleted file mode 100644 index d213f03a..00000000 --- a/docs/assets/js/4ec89f2d.d296e41b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[4819],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,a,r=function(e,t){if(null==e)return{};var n,a,r={},o=Object.keys(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a<o.length;a++)n=o[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),s=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=s(e.components);return a.createElement(l.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),d=s(n),u=r,h=d["".concat(l,".").concat(u)]||d[u]||m[u]||o;return n?a.createElement(h,i(i({ref:t},p),{},{components:n})):a.createElement(h,i({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:r,i[1]=c;for(var s=2;s<o;s++)i[s]=n[s];return a.createElement.apply(null,i)}return a.createElement.apply(null,n)}d.displayName="MDXCreateElement"},3540:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>s});var a=n(7896),r=(n(2784),n(876));const o={sidebar_position:5},i="Dispatch Command",c={unversionedId:"js-commands/dispatch-cmd",id:"js-commands/dispatch-cmd",title:"Dispatch Command",description:"The dispatch command dispatches a DOM event from the target element",source:"@site/docs/11-js-commands/dispatch-cmd.md",sourceDirName:"11-js-commands",slug:"/js-commands/dispatch-cmd",permalink:"/docs/js-commands/dispatch-cmd",draft:!1,tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"Show/Hide/Toggle Element Commands",permalink:"/docs/js-commands/show-hide-toggle-el"},next:{title:"Transition Command",permalink:"/docs/js-commands/transition-cmd"}},l={},s=[],p={toc:s};function m(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"dispatch-command"},"Dispatch Command"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"dispatch")," command dispatches a DOM event from the target element"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().dispatch(event: string, options?: DispatchOptions)\n")),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"event")," - The name of the event to dispatch"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"to")," - An optional css selector to identify the element from which to dispatch. Defaults to the element that the JS\nCommand is attached to."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"detail")," - A optional map of key/value pairs to include in the event's ",(0,r.kt)("inlineCode",{parentName:"li"},"detail")," property"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"bubbles")," - A optional boolean indicating whether the event should bubble up the DOM. Defaults to ",(0,r.kt)("inlineCode",{parentName:"li"},"true"))))),(0,r.kt)("p",null,(0,r.kt)("strong",{parentName:"p"},"Note"),': All events dispatched are of a type CustomEvent, with the exception of "click". For a "click", a MouseEvent is\ndispatched to properly simulate a UI click.'),(0,r.kt)("p",null,"For emitted CustomEvent's, the event detail will contain a dispatcher, which references the DOM node that dispatched the\nJS event to the target element."),(0,r.kt)("p",null,"Examples"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// dispatch a click\n<button phx-click="${new JS().dispatch("click", { to: "#dispatch" })}">Dispatch Click</button>\n\n// dispatch a custom event\n<button phx-click="${new JS().dispatch("custom", { to: "#dispatch", detail: { foo: "bar" } })}">\n Dispatch Custom\n</button>\n<div id="dispatch">Dispatch Target</div>\n')))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/4f66b655.9614f120.js b/docs/assets/js/4f66b655.9614f120.js deleted file mode 100644 index 9615c017..00000000 --- a/docs/assets/js/4f66b655.9614f120.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3919],{876:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>m});var i=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},a=Object.keys(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=i.createContext({}),d=function(e){var t=i.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=d(e.components);return i.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),c=d(n),m=r,v=c["".concat(p,".").concat(m)]||c[m]||u[m]||a;return n?i.createElement(v,o(o({ref:t},s),{},{components:n})):i.createElement(v,o({ref:t},s))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=c;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var d=2;d<a;d++)o[d]=n[d];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}c.displayName="MDXCreateElement"},9764:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>d});var i=n(7896),r=(n(2784),n(876));const a={sidebar_position:2},o="Built-in Image Preview",l={unversionedId:"file-upload/image-preview",id:"file-upload/image-preview",title:"Built-in Image Preview",description:"LiveViewJS ships with build-in support for image previews when uploading files.",source:"@site/docs/08-file-upload/image-preview.md",sourceDirName:"08-file-upload",slug:"/file-upload/image-preview",permalink:"/docs/file-upload/image-preview",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Overview",permalink:"/docs/file-upload/overview"},next:{title:"Built-in Drag and Drop",permalink:"/docs/file-upload/drag-and-drop"}},p={},d=[{value:"Getting Entries from <code>meta.uploads</code>",id:"getting-entries-from-metauploads",level:2},{value:"<code>live_img_preview</code> Tag",id:"live_img_preview-tag",level:2},{value:"All Together now",id:"all-together-now",level:2},{value:"Credit where credit is due",id:"credit-where-credit-is-due",level:2}],s={toc:d};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"built-in-image-preview"},"Built-in Image Preview"),(0,r.kt)("p",null,"LiveViewJS ships with build-in support for image previews when uploading files."),(0,r.kt)("h2",{id:"getting-entries-from-metauploads"},"Getting Entries from ",(0,r.kt)("inlineCode",{parentName:"h2"},"meta.uploads")),(0,r.kt)("p",null,"The list of ",(0,r.kt)("inlineCode",{parentName:"p"},"UploadEntry")," objects for a given upload config can be found in the ",(0,r.kt)("inlineCode",{parentName:"p"},"meta.uploads")," object based on the name\nyou provided when configuring it using the ",(0,r.kt)("inlineCode",{parentName:"p"},"allowUpload")," method. For example, if you configured an upload config named\n",(0,r.kt)("inlineCode",{parentName:"p"},"photos"),", you can access the list of ",(0,r.kt)("inlineCode",{parentName:"p"},"UploadEntry")," objects using ",(0,r.kt)("inlineCode",{parentName:"p"},"meta.uploads.photos"),". Here is an example of accessing\nthe list of ",(0,r.kt)("inlineCode",{parentName:"p"},"UploadEntry")," objects for a given upload config:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"...\nrender: (context, meta) => {\n ...\n <div>\n ${meta.uploads.photos.map((entry) => {\n return html`\n \x3c!-- render the entry --\x3e\n `;\n })}\n </div>\n ...\n}\n")),(0,r.kt)("h2",{id:"live_img_preview-tag"},(0,r.kt)("inlineCode",{parentName:"h2"},"live_img_preview")," Tag"),(0,r.kt)("p",null,"In order to use the built-in image preview, you must use the ",(0,r.kt)("inlineCode",{parentName:"p"},"live_img_preview")," tag. This tag takes a ",(0,r.kt)("inlineCode",{parentName:"p"},"UploadEntry")," and\nrenders an image preview of it."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"...\nrender: (context, meta) => {\n ...\n <div>${live_img_preview(entry)}</div>\n ...\n}\n")),(0,r.kt)("h2",{id:"all-together-now"},"All Together now"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"...\nrender: (context, meta) => {\n ...\n <div>\n ${meta.uploads.photos.map((entry) => {\n return html`\n <div>${live_img_preview(entry)}</div>\n `;\n })}\n </div>\n ...\n}\n")),(0,r.kt)("p",null,"That's it! \ud83e\udd2f"),(0,r.kt)("h2",{id:"credit-where-credit-is-due"},"Credit where credit is due"),(0,r.kt)("p",null,"Thanks to the Phoenix LiveView folks that built this! \ud83d\ude4c This is a great example of why we built on top of the existing\nLiveView client-side JS."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/5674.2cada8ea.js b/docs/assets/js/5674.2cada8ea.js deleted file mode 100644 index e6d5d441..00000000 --- a/docs/assets/js/5674.2cada8ea.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5674],{876:(e,t,n)=>{"use strict";n.d(t,{Zo:()=>u,kt:()=>p});var o=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function c(e,t){if(null==e)return{};var n,o,a=function(e,t){if(null==e)return{};var n,o,a={},r=Object.keys(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=o.createContext({}),s=function(e){var t=o.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=s(e.components);return o.createElement(i.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,i=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),d=s(n),p=a,f=d["".concat(i,".").concat(p)]||d[p]||m[p]||r;return n?o.createElement(f,l(l({ref:t},u),{},{components:n})):o.createElement(f,l({ref:t},u))}));function p(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,l=new Array(r);l[0]=d;var c={};for(var i in t)hasOwnProperty.call(t,i)&&(c[i]=t[i]);c.originalType=e,c.mdxType="string"==typeof e?e:a,l[1]=c;for(var s=2;s<r;s++)l[s]=n[s];return o.createElement.apply(null,l)}return o.createElement.apply(null,n)}d.displayName="MDXCreateElement"},5040:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var o=n(2784),a=n(1077),r=n(211),l=n(7896),c=n(6277);const i="iconEdit_UohW";function s(e){let{className:t,...n}=e;return o.createElement("svg",(0,l.Z)({fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,c.Z)(i,t),"aria-hidden":"true"},n),o.createElement("g",null,o.createElement("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})))}function u(e){let{editUrl:t}=e;return o.createElement("a",{href:t,target:"_blank",rel:"noreferrer noopener",className:r.k.common.editThisPage},o.createElement(s,null),o.createElement(a.Z,{id:"theme.common.editThisPage",description:"The link label to edit the current page"},"Edit this page"))}},3851:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var o=n(7896),a=n(2784),r=n(6277),l=n(1077),c=n(7683);const i="anchorWithStickyNavbar_fF9Z",s="anchorWithHideOnScrollNavbar_Yh18";function u(e){let{as:t,id:n,...u}=e;const{navbar:{hideOnScroll:m}}=(0,c.L)();return"h1"!==t&&n?a.createElement(t,(0,o.Z)({},u,{className:(0,r.Z)("anchor",m?s:i),id:n}),u.children,a.createElement("a",{className:"hash-link",href:"#"+n,title:(0,l.I)({id:"theme.common.headingLinkTitle",message:"Direct link to heading",description:"Title for link to heading"})},"\u200b")):a.createElement(t,(0,o.Z)({},u,{id:void 0}))}},8632:(e,t,n)=>{"use strict";n.d(t,{Z:()=>ye});var o=n(2784),a=n(876),r=n(7896),l=n(9854);var c=n(9741),i=n(6277),s=n(361),u=n(7683);function m(){const{prism:e}=(0,u.L)(),{colorMode:t}=(0,s.I)(),n=e.theme,o=e.darkTheme||n;return"dark"===t?o:n}var d=n(211),p=n(4501),f=n.n(p);const h=/title=(?<quote>["'])(?<title>.*?)\1/,g=/\{(?<range>[\d,-]+)\}/,y={js:{start:"\\/\\/",end:""},jsBlock:{start:"\\/\\*",end:"\\*\\/"},jsx:{start:"\\{\\s*\\/\\*",end:"\\*\\/\\s*\\}"},bash:{start:"#",end:""},html:{start:"\x3c!--",end:"--\x3e"}};function v(e,t){const n=e.map((e=>{const{start:n,end:o}=y[e];return"(?:"+n+"\\s*("+t.flatMap((e=>{var t,n;return[e.line,null==(t=e.block)?void 0:t.start,null==(n=e.block)?void 0:n.end].filter(Boolean)})).join("|")+")\\s*"+o+")"})).join("|");return new RegExp("^\\s*(?:"+n+")\\s*$")}function b(e,t){let n=e.replace(/\n$/,"");const{language:o,magicComments:a,metastring:r}=t;if(r&&g.test(r)){const e=r.match(g).groups.range;if(0===a.length)throw new Error("A highlight range has been given in code block's metastring (``` "+r+"), but no magic comment config is available. Docusaurus applies the first magic comment entry's className for metastring ranges.");const t=a[0].className,o=f()(e).filter((e=>e>0)).map((e=>[e-1,[t]]));return{lineClassNames:Object.fromEntries(o),code:n}}if(void 0===o)return{lineClassNames:{},code:n};const l=function(e,t){switch(e){case"js":case"javascript":case"ts":case"typescript":return v(["js","jsBlock"],t);case"jsx":case"tsx":return v(["js","jsBlock","jsx"],t);case"html":return v(["js","jsBlock","html"],t);case"python":case"py":case"bash":return v(["bash"],t);case"markdown":case"md":return v(["html","jsx","bash"],t);default:return v(Object.keys(y),t)}}(o,a),c=n.split("\n"),i=Object.fromEntries(a.map((e=>[e.className,{start:0,range:""}]))),s=Object.fromEntries(a.filter((e=>e.line)).map((e=>{let{className:t,line:n}=e;return[n,t]}))),u=Object.fromEntries(a.filter((e=>e.block)).map((e=>{let{className:t,block:n}=e;return[n.start,t]}))),m=Object.fromEntries(a.filter((e=>e.block)).map((e=>{let{className:t,block:n}=e;return[n.end,t]})));for(let p=0;p<c.length;){const e=c[p].match(l);if(!e){p+=1;continue}const t=e.slice(1).find((e=>void 0!==e));s[t]?i[s[t]].range+=p+",":u[t]?i[u[t]].start=p:m[t]&&(i[m[t]].range+=i[m[t]].start+"-"+(p-1)+","),c.splice(p,1)}n=c.join("\n");const d={};return Object.entries(i).forEach((e=>{let[t,{range:n}]=e;f()(n).forEach((e=>{null!=d[e]||(d[e]=[]),d[e].push(t)}))})),{lineClassNames:d,code:n}}const E="codeBlockContainer_ZGJx";function k(e){let{as:t,...n}=e;const a=function(e){const t={color:"--prism-color",backgroundColor:"--prism-background-color"},n={};return Object.entries(e.plain).forEach((e=>{let[o,a]=e;const r=t[o];r&&"string"==typeof a&&(n[r]=a)})),n}(m());return o.createElement(t,(0,r.Z)({},n,{style:a,className:(0,i.Z)(n.className,E,d.k.common.codeBlock)}))}const N={codeBlockContent:"codeBlockContent_qZBB",codeBlockTitle:"codeBlockTitle_zAEH",codeBlock:"codeBlock_TAPP",codeBlockStandalone:"codeBlockStandalone_K9VJ",codeBlockLines:"codeBlockLines_AdAo",codeBlockLinesWithNumbering:"codeBlockLinesWithNumbering_p5De",buttonGroup:"buttonGroup_TNwR"};function C(e){let{children:t,className:n}=e;return o.createElement(k,{as:"pre",tabIndex:0,className:(0,i.Z)(N.codeBlockStandalone,"thin-scrollbar",n)},o.createElement("code",{className:N.codeBlockLines},t))}var w=n(6335);const B={attributes:!0,characterData:!0,childList:!0,subtree:!0};function Z(e,t){const[n,a]=(0,o.useState)(),r=(0,o.useCallback)((()=>{var t;a(null==(t=e.current)?void 0:t.closest("[role=tabpanel][hidden]"))}),[e,a]);(0,o.useEffect)((()=>{r()}),[r]),function(e,t,n){void 0===n&&(n=B);const a=(0,w.zX)(t),r=(0,w.Ql)(n);(0,o.useEffect)((()=>{const t=new MutationObserver(a);return e&&t.observe(e,r),()=>t.disconnect()}),[e,a,r])}(n,(e=>{e.forEach((e=>{"attributes"===e.type&&"hidden"===e.attributeName&&(t(),r())}))}),{attributes:!0,characterData:!1,childList:!1,subtree:!1})}const T={plain:{backgroundColor:"#2a2734",color:"#9a86fd"},styles:[{types:["comment","prolog","doctype","cdata","punctuation"],style:{color:"#6c6783"}},{types:["namespace"],style:{opacity:.7}},{types:["tag","operator","number"],style:{color:"#e09142"}},{types:["property","function"],style:{color:"#9a86fd"}},{types:["tag-id","selector","atrule-id"],style:{color:"#eeebff"}},{types:["attr-name"],style:{color:"#c4b9fe"}},{types:["boolean","string","entity","url","attr-value","keyword","control","directive","unit","statement","regex","atrule","placeholder","variable"],style:{color:"#ffcc99"}},{types:["deleted"],style:{textDecorationLine:"line-through"}},{types:["inserted"],style:{textDecorationLine:"underline"}},{types:["italic"],style:{fontStyle:"italic"}},{types:["important","bold"],style:{fontWeight:"bold"}},{types:["important"],style:{color:"#c4b9fe"}}]};var j={Prism:n(7175).Z,theme:T};function _(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function O(){return O=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},O.apply(this,arguments)}var x=/\r\n|\r|\n/,L=function(e){0===e.length?e.push({types:["plain"],content:"\n",empty:!0}):1===e.length&&""===e[0].content&&(e[0].content="\n",e[0].empty=!0)},S=function(e,t){var n=e.length;return n>0&&e[n-1]===t?e:e.concat(t)},P=function(e,t){var n=e.plain,o=Object.create(null),a=e.styles.reduce((function(e,n){var o=n.languages,a=n.style;return o&&!o.includes(t)||n.types.forEach((function(t){var n=O({},e[t],a);e[t]=n})),e}),o);return a.root=n,a.plain=O({},n,{backgroundColor:null}),a};function z(e,t){var n={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&-1===t.indexOf(o)&&(n[o]=e[o]);return n}const A=function(e){function t(){for(var t=this,n=[],o=arguments.length;o--;)n[o]=arguments[o];e.apply(this,n),_(this,"getThemeDict",(function(e){if(void 0!==t.themeDict&&e.theme===t.prevTheme&&e.language===t.prevLanguage)return t.themeDict;t.prevTheme=e.theme,t.prevLanguage=e.language;var n=e.theme?P(e.theme,e.language):void 0;return t.themeDict=n})),_(this,"getLineProps",(function(e){var n=e.key,o=e.className,a=e.style,r=O({},z(e,["key","className","style","line"]),{className:"token-line",style:void 0,key:void 0}),l=t.getThemeDict(t.props);return void 0!==l&&(r.style=l.plain),void 0!==a&&(r.style=void 0!==r.style?O({},r.style,a):a),void 0!==n&&(r.key=n),o&&(r.className+=" "+o),r})),_(this,"getStyleForToken",(function(e){var n=e.types,o=e.empty,a=n.length,r=t.getThemeDict(t.props);if(void 0!==r){if(1===a&&"plain"===n[0])return o?{display:"inline-block"}:void 0;if(1===a&&!o)return r[n[0]];var l=o?{display:"inline-block"}:{},c=n.map((function(e){return r[e]}));return Object.assign.apply(Object,[l].concat(c))}})),_(this,"getTokenProps",(function(e){var n=e.key,o=e.className,a=e.style,r=e.token,l=O({},z(e,["key","className","style","token"]),{className:"token "+r.types.join(" "),children:r.content,style:t.getStyleForToken(r),key:void 0});return void 0!==a&&(l.style=void 0!==l.style?O({},l.style,a):a),void 0!==n&&(l.key=n),o&&(l.className+=" "+o),l})),_(this,"tokenize",(function(e,t,n,o){var a={code:t,grammar:n,language:o,tokens:[]};e.hooks.run("before-tokenize",a);var r=a.tokens=e.tokenize(a.code,a.grammar,a.language);return e.hooks.run("after-tokenize",a),r}))}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.render=function(){var e=this.props,t=e.Prism,n=e.language,o=e.code,a=e.children,r=this.getThemeDict(this.props),l=t.languages[n];return a({tokens:function(e){for(var t=[[]],n=[e],o=[0],a=[e.length],r=0,l=0,c=[],i=[c];l>-1;){for(;(r=o[l]++)<a[l];){var s=void 0,u=t[l],m=n[l][r];if("string"==typeof m?(u=l>0?u:["plain"],s=m):(u=S(u,m.type),m.alias&&(u=S(u,m.alias)),s=m.content),"string"==typeof s){var d=s.split(x),p=d.length;c.push({types:u,content:d[0]});for(var f=1;f<p;f++)L(c),i.push(c=[]),c.push({types:u,content:d[f]})}else l++,t.push(u),n.push(s),o.push(0),a.push(s.length)}l--,t.pop(),n.pop(),o.pop(),a.pop()}return L(c),i}(void 0!==l?this.tokenize(t,o,l,n):[o]),className:"prism-code language-"+n,style:void 0!==r?r.root:{},getLineProps:this.getLineProps,getTokenProps:this.getTokenProps})},t}(o.Component),I="codeLine_DPDv",D="codeLineNumber_YxQB",M="codeLineContent_SOIp";function H(e){let{line:t,classNames:n,showLineNumbers:a,getLineProps:l,getTokenProps:c}=e;1===t.length&&"\n"===t[0].content&&(t[0].content="");const s=l({line:t,className:(0,i.Z)(n,a&&I)}),u=t.map(((e,t)=>o.createElement("span",(0,r.Z)({key:t},c({token:e,key:t})))));return o.createElement("span",s,a?o.createElement(o.Fragment,null,o.createElement("span",{className:D}),o.createElement("span",{className:M},u)):o.createElement(o.Fragment,null,u,o.createElement("br",null)))}var R=n(1077);const V={copyButtonCopied:"copyButtonCopied_Mzdr",copyButtonIcons:"copyButtonIcons_MVhB",copyButtonIcon:"copyButtonIcon_yxgH",copyButtonSuccessIcon:"copyButtonSuccessIcon_QJLJ"};function W(e){let{code:t,className:n}=e;const[a,r]=(0,o.useState)(!1),l=(0,o.useRef)(void 0),c=(0,o.useCallback)((()=>{!function(e,t){let{target:n=document.body}=void 0===t?{}:t;const o=document.createElement("textarea"),a=document.activeElement;o.value=e,o.setAttribute("readonly",""),o.style.contain="strict",o.style.position="absolute",o.style.left="-9999px",o.style.fontSize="12pt";const r=document.getSelection();let l=!1;r.rangeCount>0&&(l=r.getRangeAt(0)),n.append(o),o.select(),o.selectionStart=0,o.selectionEnd=e.length;let c=!1;try{c=document.execCommand("copy")}catch{}o.remove(),l&&(r.removeAllRanges(),r.addRange(l)),a&&a.focus()}(t),r(!0),l.current=window.setTimeout((()=>{r(!1)}),1e3)}),[t]);return(0,o.useEffect)((()=>()=>window.clearTimeout(l.current)),[]),o.createElement("button",{type:"button","aria-label":a?(0,R.I)({id:"theme.CodeBlock.copied",message:"Copied",description:"The copied button label on code blocks"}):(0,R.I)({id:"theme.CodeBlock.copyButtonAriaLabel",message:"Copy code to clipboard",description:"The ARIA label for copy code blocks button"}),title:(0,R.I)({id:"theme.CodeBlock.copy",message:"Copy",description:"The copy button label on code blocks"}),className:(0,i.Z)("clean-btn",n,V.copyButton,a&&V.copyButtonCopied),onClick:c},o.createElement("span",{className:V.copyButtonIcons,"aria-hidden":"true"},o.createElement("svg",{className:V.copyButtonIcon,viewBox:"0 0 24 24"},o.createElement("path",{d:"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"})),o.createElement("svg",{className:V.copyButtonSuccessIcon,viewBox:"0 0 24 24"},o.createElement("path",{d:"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"}))))}const F="wordWrapButtonIcon_SMj9",q="wordWrapButtonEnabled_vR9E";function U(e){let{className:t,onClick:n,isEnabled:a}=e;const r=(0,R.I)({id:"theme.CodeBlock.wordWrapToggle",message:"Toggle word wrap",description:"The title attribute for toggle word wrapping button of code block lines"});return o.createElement("button",{type:"button",onClick:n,className:(0,i.Z)("clean-btn",t,a&&q),"aria-label":r,title:r},o.createElement("svg",{className:F,viewBox:"0 0 24 24","aria-hidden":"true"},o.createElement("path",{fill:"currentColor",d:"M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"})))}function G(e){var t;let{children:n,className:a="",metastring:l,title:c,showLineNumbers:s,language:d}=e;const{prism:{defaultLanguage:p,magicComments:f}}=(0,u.L)(),g=null!=(t=null!=d?d:function(e){const t=e.split(" ").find((e=>e.startsWith("language-")));return null==t?void 0:t.replace(/language-/,"")}(a))?t:p,y=m(),v=function(){const[e,t]=(0,o.useState)(!1),[n,a]=(0,o.useState)(!1),r=(0,o.useRef)(null),l=(0,o.useCallback)((()=>{const n=r.current.querySelector("code");e?n.removeAttribute("style"):(n.style.whiteSpace="pre-wrap",n.style.overflowWrap="anywhere"),t((e=>!e))}),[r,e]),c=(0,o.useCallback)((()=>{const{scrollWidth:e,clientWidth:t}=r.current,n=e>t||r.current.querySelector("code").hasAttribute("style");a(n)}),[r]);return Z(r,c),(0,o.useEffect)((()=>{c()}),[e,c]),(0,o.useEffect)((()=>(window.addEventListener("resize",c,{passive:!0}),()=>{window.removeEventListener("resize",c)})),[c]),{codeBlockRef:r,isEnabled:e,isCodeScrollable:n,toggle:l}}(),E=function(e){var t,n;return null!=(t=null==e||null==(n=e.match(h))?void 0:n.groups.title)?t:""}(l)||c,{lineClassNames:C,code:w}=b(n,{metastring:l,language:g,magicComments:f}),B=null!=s?s:function(e){return Boolean(null==e?void 0:e.includes("showLineNumbers"))}(l);return o.createElement(k,{as:"div",className:(0,i.Z)(a,g&&!a.includes("language-"+g)&&"language-"+g)},E&&o.createElement("div",{className:N.codeBlockTitle},E),o.createElement("div",{className:N.codeBlockContent},o.createElement(A,(0,r.Z)({},j,{theme:y,code:w,language:null!=g?g:"text"}),(e=>{let{className:t,tokens:n,getLineProps:a,getTokenProps:r}=e;return o.createElement("pre",{tabIndex:0,ref:v.codeBlockRef,className:(0,i.Z)(t,N.codeBlock,"thin-scrollbar")},o.createElement("code",{className:(0,i.Z)(N.codeBlockLines,B&&N.codeBlockLinesWithNumbering)},n.map(((e,t)=>o.createElement(H,{key:t,line:e,getLineProps:a,getTokenProps:r,classNames:C[t],showLineNumbers:B})))))})),o.createElement("div",{className:N.buttonGroup},(v.isEnabled||v.isCodeScrollable)&&o.createElement(U,{className:N.codeButton,onClick:()=>v.toggle(),isEnabled:v.isEnabled}),o.createElement(W,{className:N.codeButton,code:w}))))}function Y(e){let{children:t,...n}=e;const a=(0,c.Z)(),l=function(e){return o.Children.toArray(e).some((e=>(0,o.isValidElement)(e)))?e:Array.isArray(e)?e.join(""):e}(t),i="string"==typeof l?G:C;return o.createElement(i,(0,r.Z)({key:String(a)},n),l)}var J=n(9817);var Q=n(8698);const $="details_B4FW",X="isBrowser_Cof9",K="collapsibleContent_VYua";function ee(e){return!!e&&("SUMMARY"===e.tagName||ee(e.parentElement))}function te(e,t){return!!e&&(e===t||te(e.parentElement,t))}function ne(e){let{summary:t,children:n,...a}=e;const l=(0,c.Z)(),s=(0,o.useRef)(null),{collapsed:u,setCollapsed:m}=(0,Q.u)({initialState:!a.open}),[d,p]=(0,o.useState)(a.open);return o.createElement("details",(0,r.Z)({},a,{ref:s,open:d,"data-collapsed":u,className:(0,i.Z)($,l&&X,a.className),onMouseDown:e=>{ee(e.target)&&e.detail>1&&e.preventDefault()},onClick:e=>{e.stopPropagation();const t=e.target;ee(t)&&te(t,s.current)&&(e.preventDefault(),u?(m(!1),p(!0)):m(!0))}}),null!=t?t:o.createElement("summary",null,"Details"),o.createElement(Q.z,{lazy:!1,collapsed:u,disableSSRStyle:!0,onCollapseTransitionEnd:e=>{m(e),p(!e)}},o.createElement("div",{className:K},n)))}const oe="details_SZgV";function ae(e){let{...t}=e;return o.createElement(ne,(0,r.Z)({},t,{className:(0,i.Z)("alert alert--info",oe,t.className)}))}var re=n(3851);function le(e){return o.createElement(re.Z,e)}const ce="containsTaskList__YnT";const ie="img__Ss2";const se="admonition_qNG0",ue="admonitionHeading_l909",me="admonitionIcon_UNbs",de="admonitionContent_oUmQ";const pe={note:{infimaClassName:"secondary",iconComponent:function(){return o.createElement("svg",{viewBox:"0 0 14 16"},o.createElement("path",{fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"}))},label:o.createElement(R.Z,{id:"theme.admonition.note",description:"The default label used for the Note admonition (:::note)"},"note")},tip:{infimaClassName:"success",iconComponent:function(){return o.createElement("svg",{viewBox:"0 0 12 16"},o.createElement("path",{fillRule:"evenodd",d:"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"}))},label:o.createElement(R.Z,{id:"theme.admonition.tip",description:"The default label used for the Tip admonition (:::tip)"},"tip")},danger:{infimaClassName:"danger",iconComponent:function(){return o.createElement("svg",{viewBox:"0 0 12 16"},o.createElement("path",{fillRule:"evenodd",d:"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"}))},label:o.createElement(R.Z,{id:"theme.admonition.danger",description:"The default label used for the Danger admonition (:::danger)"},"danger")},info:{infimaClassName:"info",iconComponent:function(){return o.createElement("svg",{viewBox:"0 0 14 16"},o.createElement("path",{fillRule:"evenodd",d:"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"}))},label:o.createElement(R.Z,{id:"theme.admonition.info",description:"The default label used for the Info admonition (:::info)"},"info")},caution:{infimaClassName:"warning",iconComponent:function(){return o.createElement("svg",{viewBox:"0 0 16 16"},o.createElement("path",{fillRule:"evenodd",d:"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"}))},label:o.createElement(R.Z,{id:"theme.admonition.caution",description:"The default label used for the Caution admonition (:::caution)"},"caution")}},fe={secondary:"note",important:"info",success:"tip",warning:"danger"};function he(e){var t;const{mdxAdmonitionTitle:n,rest:a}=function(e){const t=o.Children.toArray(e),n=t.find((e=>{var t;return o.isValidElement(e)&&"mdxAdmonitionTitle"===(null==(t=e.props)?void 0:t.mdxType)})),a=o.createElement(o.Fragment,null,t.filter((e=>e!==n)));return{mdxAdmonitionTitle:n,rest:a}}(e.children);return{...e,title:null!=(t=e.title)?t:n,children:a}}const ge={head:function(e){const t=o.Children.map(e.children,(e=>o.isValidElement(e)?function(e){var t;if(null!=(t=e.props)&&t.mdxType&&e.props.originalType){const{mdxType:t,originalType:n,...a}=e.props;return o.createElement(e.props.originalType,a)}return e}(e):e));return o.createElement(l.Z,e,t)},code:function(e){const t=["a","b","big","i","span","em","strong","sup","sub","small"];return o.Children.toArray(e.children).every((e=>{var n;return"string"==typeof e&&!e.includes("\n")||(0,o.isValidElement)(e)&&t.includes(null==(n=e.props)?void 0:n.mdxType)}))?o.createElement("code",e):o.createElement(Y,e)},a:function(e){return o.createElement(J.Z,e)},pre:function(e){var t;return o.createElement(Y,(0,o.isValidElement)(e.children)&&"code"===(null==(t=e.children.props)?void 0:t.originalType)?e.children.props:{...e})},details:function(e){const t=o.Children.toArray(e.children),n=t.find((e=>{var t;return o.isValidElement(e)&&"summary"===(null==(t=e.props)?void 0:t.mdxType)})),a=o.createElement(o.Fragment,null,t.filter((e=>e!==n)));return o.createElement(ae,(0,r.Z)({},e,{summary:n}),a)},ul:function(e){return o.createElement("ul",(0,r.Z)({},e,{className:(t=e.className,(0,i.Z)(t,(null==t?void 0:t.includes("contains-task-list"))&&ce))}));var t},img:function(e){return o.createElement("img",(0,r.Z)({loading:"lazy"},e,{className:(t=e.className,(0,i.Z)(t,ie))}));var t},h1:e=>o.createElement(le,(0,r.Z)({as:"h1"},e)),h2:e=>o.createElement(le,(0,r.Z)({as:"h2"},e)),h3:e=>o.createElement(le,(0,r.Z)({as:"h3"},e)),h4:e=>o.createElement(le,(0,r.Z)({as:"h4"},e)),h5:e=>o.createElement(le,(0,r.Z)({as:"h5"},e)),h6:e=>o.createElement(le,(0,r.Z)({as:"h6"},e)),admonition:function(e){const{children:t,type:n,title:a,icon:r}=he(e),l=function(e){var t;const n=null!=(t=fe[e])?t:e;return pe[n]||(console.warn('No admonition config found for admonition type "'+n+'". Using Info as fallback.'),pe.info)}(n),c=null!=a?a:l.label,{iconComponent:s}=l,u=null!=r?r:o.createElement(s,null);return o.createElement("div",{className:(0,i.Z)(d.k.common.admonition,d.k.common.admonitionType(e.type),"alert","alert--"+l.infimaClassName,se)},o.createElement("div",{className:ue},o.createElement("span",{className:me},u),c),o.createElement("div",{className:de},t))}};function ye(e){let{children:t}=e;return o.createElement(a.Zo,{components:ge},t)}},7066:(e,t,n)=>{"use strict";n.d(t,{Z:()=>l});var o=n(2784),a=n(6277),r=n(9817);function l(e){const{permalink:t,title:n,subLabel:l,isNext:c}=e;return o.createElement(r.Z,{className:(0,a.Z)("pagination-nav__link",c?"pagination-nav__link--next":"pagination-nav__link--prev"),to:t},l&&o.createElement("div",{className:"pagination-nav__sublabel"},l),o.createElement("div",{className:"pagination-nav__label"},n))}},4178:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var o=n(2784),a=n(6277),r=n(9817);const l="tag_qE9H",c="tagRegular_aHXt",i="tagWithCount_UC8q";function s(e){let{permalink:t,label:n,count:s}=e;return o.createElement(r.Z,{href:t,className:(0,a.Z)(l,s?i:c)},n,s&&o.createElement("span",null,s))}},2569:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var o=n(2784),a=n(6277),r=n(1077),l=n(4178);const c="tags_q74f",i="tag_lSC7";function s(e){let{tags:t}=e;return o.createElement(o.Fragment,null,o.createElement("b",null,o.createElement(r.Z,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list"},"Tags:")),o.createElement("ul",{className:(0,a.Z)(c,"padding--none","margin-left--sm")},t.map((e=>{let{label:t,permalink:n}=e;return o.createElement("li",{key:n,className:i},o.createElement(l.Z,{label:t,permalink:n}))}))))}},4501:(e,t)=>{function n(e){let t,n=[];for(let o of e.split(",").map((e=>e.trim())))if(/^-?\d+$/.test(o))n.push(parseInt(o,10));else if(t=o.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)){let[e,o,a,r]=t;if(o&&r){o=parseInt(o),r=parseInt(r);const e=o<r?1:-1;"-"!==a&&".."!==a&&"\u2025"!==a||(r+=e);for(let t=o;t!==r;t+=e)n.push(t)}}return n}t.default=n,e.exports=n}}]); \ No newline at end of file diff --git a/docs/assets/js/59362658.4deb710c.js b/docs/assets/js/59362658.4deb710c.js deleted file mode 100644 index 687bea09..00000000 --- a/docs/assets/js/59362658.4deb710c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2267],{876:(e,t,r)=>{r.d(t,{Zo:()=>i,kt:()=>f});var o=r(2784);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function l(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?a(Object(r),!0).forEach((function(t){n(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):a(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,o,n=function(e,t){if(null==e)return{};var r,o,n={},a=Object.keys(e);for(o=0;o<a.length;o++)r=a[o],t.indexOf(r)>=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)r=a[o],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var c=o.createContext({}),u=function(e){var t=o.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},i=function(e){var t=u(e.components);return o.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,a=e.originalType,c=e.parentName,i=s(e,["components","mdxType","originalType","parentName"]),m=u(r),f=n,d=m["".concat(c,".").concat(f)]||m[f]||p[f]||a;return r?o.createElement(d,l(l({ref:t},i),{},{components:r})):o.createElement(d,l({ref:t},i))}));function f(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var a=r.length,l=new Array(a);l[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:n,l[1]=s;for(var u=2;u<a;u++)l[u]=r[u];return o.createElement.apply(null,l)}return o.createElement.apply(null,r)}m.displayName="MDXCreateElement"},1987:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>u});var o=r(7896),n=(r(2784),r(876));const a={slug:"mdx-blog-post",title:"MDX Blog Post",authors:["floodfx"],tags:["docusaurus"]},l=void 0,s={permalink:"/blog/mdx-blog-post",source:"@site/blog/2021-08-01-mdx-blog-post.mdx",title:"MDX Blog Post",description:"Blog posts support Docusaurus Markdown features, such as MDX.",date:"2021-08-01T00:00:00.000Z",formattedDate:"August 1, 2021",tags:[{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:.175,hasTruncateMarker:!1,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"mdx-blog-post",title:"MDX Blog Post",authors:["floodfx"],tags:["docusaurus"]},prevItem:{title:"Welcome",permalink:"/blog/welcome"},nextItem:{title:"Long Blog Post",permalink:"/blog/long-blog-post"}},c={authorsImageUrls:[void 0]},u=[],i={toc:u};function p(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},i,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Blog posts support ",(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/markdown-features"},"Docusaurus Markdown features"),", such as ",(0,n.kt)("a",{parentName:"p",href:"https://mdxjs.com/"},"MDX"),"."),(0,n.kt)("admonition",{type:"tip"},(0,n.kt)("p",{parentName:"admonition"},"Use the power of React to create interactive blog posts."),(0,n.kt)("pre",{parentName:"admonition"},(0,n.kt)("code",{parentName:"pre",className:"language-js"},'<button onClick={() => alert("button clicked!")}>Click me!</button>\n')),(0,n.kt)("button",{onClick:()=>alert("button clicked!")},"Click me!")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/5f30c8bb.5f093f7f.js b/docs/assets/js/5f30c8bb.5f093f7f.js deleted file mode 100644 index d6456f07..00000000 --- a/docs/assets/js/5f30c8bb.5f093f7f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8259],{876:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(2784);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function l(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var s=n.createContext({}),p=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(r),m=a,h=d["".concat(s,".").concat(m)]||d[m]||c[m]||o;return r?n.createElement(h,i(i({ref:t},u),{},{components:r})):n.createElement(h,i({ref:t},u))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var p=2;p<o;p++)i[p]=r[p];return n.createElement.apply(null,i)}return n.createElement.apply(null,r)}d.displayName="MDXCreateElement"},3831:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var n=r(7896),a=(r(2784),r(876));const o={sidebar_position:4},i="Deno - Run the Examples",l={unversionedId:"quick-starts/deno-run-examples",id:"quick-starts/deno-run-examples",title:"Deno - Run the Examples",description:"LiveViewJS ships with over a dozen example LiveViews that show everything from simple button-based events to",source:"@site/docs/02-quick-starts/deno-run-examples.md",sourceDirName:"02-quick-starts",slug:"/quick-starts/deno-run-examples",permalink:"/docs/quick-starts/deno-run-examples",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"NodeJS - Build a LiveView",permalink:"/docs/quick-starts/nodejs-build-first-liveview"},next:{title:"Deno - Build a LiveView",permalink:"/docs/quick-starts/deno-build-first-liveview"}},s={},p=[{value:"Prerequisite",id:"prerequisite",level:2},{value:"Run the Examples",id:"run-the-examples",level:2},{value:"Explore the Examples",id:"explore-the-examples",level:2}],u={toc:p};function c(e){let{components:t,...o}=e;return(0,a.kt)("wrapper",(0,n.Z)({},u,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"deno---run-the-examples"},"Deno - Run the Examples"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," ships with over a dozen example LiveViews that show everything from simple button-based events to\nreal-time, multi-player views. It takes approximately 1 minute to get these examples up and running and is a good way to\nget a feel for the user experience of a LiveView. Let's get started!"),(0,a.kt)("h2",{id:"prerequisite"},"Prerequisite"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://deno.land/"},"Deno")," version 1.24.x or above. (Older versions may work but haven't been tested.)"),(0,a.kt)("p",null,"If you haven't already, ",(0,a.kt)("a",{parentName:"p",href:"get-liveviewjs-repo"},"download the ",(0,a.kt)("strong",{parentName:"a"},"LiveViewJS")," repo"),"."),(0,a.kt)("h2",{id:"run-the-examples"},"Run the Examples"),(0,a.kt)("p",null,"Navigate to the ",(0,a.kt)("inlineCode",{parentName:"p"},"packages/deno")," directory:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"# cd to the deno directory\ncd packages/deno\n")),(0,a.kt)("p",null,"Then, start the Deno server with the examples:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"deno run --allow-run --allow-read --allow-env src/example/autorun.ts\n")),(0,a.kt)("p",null,"Point your browser to ",(0,a.kt)("a",{parentName:"p",href:"http://localhost:9001"},"http://localhost:9001")),(0,a.kt)("h2",{id:"explore-the-examples"},"Explore the Examples"),(0,a.kt)("p",null,"You should see something like the screenshot below including a list of examples with a brief description, a link to the\nrunning LiveView, and a link to the source code for each example."),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"LiveViewJS Examples Screenshot",src:r(7381).Z,width:"1552",height:"1382"})))}c.isMDXComponent=!0},7381:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/liveviewjs_examples_rec-2cc096a7ff8f675e44f0a7fa0c1108c7.gif"}}]); \ No newline at end of file diff --git a/docs/assets/js/608ae6a4.5be44012.js b/docs/assets/js/608ae6a4.5be44012.js deleted file mode 100644 index 89278d6f..00000000 --- a/docs/assets/js/608ae6a4.5be44012.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6938],{4545:e=>{e.exports=JSON.parse('{"permalink":"/blog/tags/docusaurus","page":1,"postsPerPage":10,"totalPages":1,"totalCount":4,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/630c0ad8.bc45891b.js b/docs/assets/js/630c0ad8.bc45891b.js deleted file mode 100644 index 32f29ee3..00000000 --- a/docs/assets/js/630c0ad8.bc45891b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7911],{876:(e,t,n)=>{n.d(t,{Zo:()=>h,kt:()=>u});var i=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?s(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):s(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function o(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},s=Object.keys(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},h=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},p=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,s=e.originalType,l=e.parentName,h=o(e,["components","mdxType","originalType","parentName"]),p=c(n),u=a,v=p["".concat(l,".").concat(u)]||p[u]||d[u]||s;return n?i.createElement(v,r(r({ref:t},h),{},{components:n})):i.createElement(v,r({ref:t},h))}));function u(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var s=n.length,r=new Array(s);r[0]=p;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o.mdxType="string"==typeof e?e:a,r[1]=o;for(var c=2;c<s;c++)r[c]=n[c];return i.createElement.apply(null,r)}return i.createElement.apply(null,n)}p.displayName="MDXCreateElement"},1217:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>s,metadata:()=>o,toc:()=>c});var i=n(7896),a=(n(2784),n(876));const s={sidebar_position:1},r="Lifecycle of a LiveView",o={unversionedId:"lifecycle-of-a-liveview/intro",id:"lifecycle-of-a-liveview/intro",title:"Lifecycle of a LiveView",description:"We are going to look at the lifecycle of LiveViews in detail to see when each LiveView method (e.g., mount,",source:"@site/docs/05-lifecycle-of-a-liveview/intro.md",sourceDirName:"05-lifecycle-of-a-liveview",slug:"/lifecycle-of-a-liveview/intro",permalink:"/docs/lifecycle-of-a-liveview/intro",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Lifecycle of a LiveView",permalink:"/docs/category/lifecycle-of-a-liveview"},next:{title:"User Events",permalink:"/docs/category/user-events"}},l={},c=[{value:"HTTP and Websocket",id:"http-and-websocket",level:2},{value:"HTTP Request Phase",id:"http-request-phase",level:3},{value:"Advantages of HTML",id:"advantages-of-html",level:4},{value:"Websocket Phase",id:"websocket-phase",level:3},{value:"Websocket Join Phase",id:"websocket-join-phase",level:4},{value:"Interactive Phase",id:"interactive-phase",level:4},{value:"Other Processes / Phases",id:"other-processes--phases",level:3},{value:"Heartbeat",id:"heartbeat",level:4},{value:"Shutdown",id:"shutdown",level:4}],h={toc:c};function d(e){let{components:t,...s}=e;return(0,a.kt)("wrapper",(0,i.Z)({},h,s,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"lifecycle-of-a-liveview"},"Lifecycle of a LiveView"),(0,a.kt)("p",null,"We are going to look at the lifecycle of LiveViews in detail to see when each LiveView method (e.g., ",(0,a.kt)("inlineCode",{parentName:"p"},"mount"),",\n",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"render"),", etc) are called during the lifecycle so you can better understand how to use them."),(0,a.kt)("h2",{id:"http-and-websocket"},"HTTP and Websocket"),(0,a.kt)("p",null,"There are two major parts of a lifecycle:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"HTTP request phase"),(0,a.kt)("li",{parentName:"ol"},"Websocket phase")),(0,a.kt)("h3",{id:"http-request-phase"},"HTTP Request Phase"),(0,a.kt)("p",null,"Just like any other web page, all LiveViews start with a HTTP request to a URL (e.g., ",(0,a.kt)("inlineCode",{parentName:"p"},"GET /my-liveview"),") and this route\nis served by a webserver (e.g., Express). If that route is a LiveView route, the webserver hands off the request to the\n",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," library for processing."),(0,a.kt)("p",null,"The ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," library then creates a new LiveView instance and starts the HTTP request phase which consists of:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("inlineCode",{parentName:"li"},"mount")," - The LiveView is mounted and the context is initialized"),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("inlineCode",{parentName:"li"},"handleParams")," - The LiveView is given a chance to handle any params passed in the URL (e.g.\n",(0,a.kt)("inlineCode",{parentName:"li"},"GET /my-liveview?foo=bar"),")"),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("inlineCode",{parentName:"li"},"render")," - The LiveView is rendered based on the ",(0,a.kt)("inlineCode",{parentName:"li"},"context")," and the HTML is returned to the webserver")),(0,a.kt)("p",null,"The webserver then returns the HTML to the browser. Below is a sequence diagram showing the HTTP request phase:"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"Http Request Phase",src:n(3017).Z,width:"1302",height:"1093"})),(0,a.kt)("h4",{id:"advantages-of-html"},"Advantages of HTML"),(0,a.kt)("p",null,"The advantage of rendering the HTML initially is:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},'"First paint" is extremely fast (it\'s just HTML)'),(0,a.kt)("li",{parentName:"ul"},"No waiting for MBs of JS to download"),(0,a.kt)("li",{parentName:"ul"},"Renders even if JS is disabled"),(0,a.kt)("li",{parentName:"ul"},"Search engine friendly (again it is only HTML)")),(0,a.kt)("h3",{id:"websocket-phase"},"Websocket Phase"),(0,a.kt)("p",null,"After the initial HTTP request and response, the LiveView client javascript automatically connects to the LiveView\nserver via a websocket. The websocket is used to send events from the browser to the LiveView server and to receive DOM\npatches from the LiveView server. The websocket phase breaks down into three parts:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},'"Websocket Join" - Establish the websocket connection and run the initial LiveView lifecycle methods'),(0,a.kt)("li",{parentName:"ol"},'"Interactive" - The LiveView is interactive and can respond to user events or update based on server events'),(0,a.kt)("li",{parentName:"ol"},'"Shutdown" - The LiveView automatically cleans up and shuts down resources')),(0,a.kt)("h4",{id:"websocket-join-phase"},"Websocket Join Phase"),(0,a.kt)("p",null,'During the "Websocket Join" phase the LiveView runs the same initiliation methods as the HTTP request phase:'),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("inlineCode",{parentName:"li"},"mount")," - The LiveView is mounted and the context is initialized"),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("inlineCode",{parentName:"li"},"handleParams")," - The LiveView is given a chance to handle any params passed in the URL (e.g.\n",(0,a.kt)("inlineCode",{parentName:"li"},"GET /my-liveview?foo=bar"),")"),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("inlineCode",{parentName:"li"},"render")," - The LiveView is rendered based on the ",(0,a.kt)("inlineCode",{parentName:"li"},"context"))),(0,a.kt)("p",null,'But instead of sending back a full HTML page, the LiveView sends back a datastructure that breaks down the HTML into\n"static" and "dynamic" parts. This data structure allows future "diffs" to be sent to the client to update the DOM.\nBelow is a sequence diagram showing the "Websocket Join" phase:'),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"Websocket Join",src:n(1425).Z,width:"1277",height:"990"})),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"You may have noticed both the HTTP request phase and the Websocket Join phase run the same methods. This is\nbecause the LiveView is initialized (",(0,a.kt)("inlineCode",{parentName:"p"},"mount")," => ",(0,a.kt)("inlineCode",{parentName:"p"},"handleParams")," => ",(0,a.kt)("inlineCode",{parentName:"p"},"render"),") in both phases. The HTTP phase doesn't\nretain any state but the Websocket phase does keep state in memory so needs to re-run the initialization methods.\nImportantly, you may also want to handle HTTP vs Websocket differently in your LiveView so calling the initialization\nmethods in both phases is important.")),(0,a.kt)("h4",{id:"interactive-phase"},"Interactive Phase"),(0,a.kt)("p",null,'Once the Websocket has been established, the LiveView is in the "Interactive" phase. In this phase the LiveView can\nrespond to user events and server events.'),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"User events")," (clicks, form updates/submissions, keyboard input, etc) are sent from the browser to the LiveView server\nvia the websocket, routed to ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," then ",(0,a.kt)("inlineCode",{parentName:"p"},"render"),". ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS"),' then calculates the "diffs", sends those diffs\nback to the client which automatically applies them to the DOM.'),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Server events")," (from ",(0,a.kt)("inlineCode",{parentName:"p"},"socket.sendInfo")," or Pub/Sub subscriptions) are automatically received in the LiveView server,\nrouted to ",(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo")," then to ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," Similar to user events, ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS"),' then calculates the "diffs", sends those\ndiffs back to the client which automatically applies them to the DOM.'),(0,a.kt)("p",null,'Below is a sequence diagram showing the "Interactive" phase:'),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"Interactive",src:n(5168).Z,width:"1285",height:"1814"})),(0,a.kt)("h3",{id:"other-processes--phases"},"Other Processes / Phases"),(0,a.kt)("p",null,"LiveViews have a couple of other processes that are important to understand but are automatically handled by\n",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," so you don't need to worry about them."),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},'"Heartbeat" - The LiveView clientr sends a heartbeat message to the server every 30 seconds to ensure the websocket\nconnection is still alive'),(0,a.kt)("li",{parentName:"ol"},'"Shutdown" - The LiveView automatically cleans up and shuts down resources')),(0,a.kt)("p",null,'Here are a couple of sequence diagrams showing the "Heartbeat" and "Shutdown" phases:'),(0,a.kt)("h4",{id:"heartbeat"},"Heartbeat"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"Heartbeat",src:n(3473).Z,width:"852",height:"628"})),(0,a.kt)("h4",{id:"shutdown"},"Shutdown"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"Shutdown",src:n(8944).Z,width:"845",height:"816"})))}d.isMDXComponent=!0},3473:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/liveview-lifecycle-heartbeat-65d664e1b997bbc0737aee2d81e0575e.svg"},3017:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/liveview-lifecycle-http-phase-ea214711543a70a25c10e3bf28ab347b.svg"},8944:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/liveview-lifecycle-shutdown-7e64e2d957894e06dadbde8ebfce6dc0.svg"},5168:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/liveview-lifecycle-user-and-server-events-0c078ec9c402dedb80571d075a41922e.svg"},1425:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/liveview-lifecycle-websocket-join-a167e1df602ab471b5bba9187ccf6ae2.svg"}}]); \ No newline at end of file diff --git a/docs/assets/js/6468.7f78e9f0.js b/docs/assets/js/6468.7f78e9f0.js deleted file mode 100644 index 29bc9345..00000000 --- a/docs/assets/js/6468.7f78e9f0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6468],{3485:(e,t,a)=>{a.d(t,{Z:()=>N});var l=a(2784),n=a(6277),r=a(9991),o=a(7963),s=a(9817),c=a(1077);const i="sidebar_RYHo",m="sidebarItemTitle_sRjx",u="sidebarItemList_uMtB",d="sidebarItem_rygH",g="sidebarItemLink_EKgd",p="sidebarItemLinkActive_hRXJ";function h(e){let{sidebar:t}=e;return l.createElement("aside",{className:"col col--3"},l.createElement("nav",{className:(0,n.Z)(i,"thin-scrollbar"),"aria-label":(0,c.I)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"})},l.createElement("div",{className:(0,n.Z)(m,"margin-bottom--md")},t.title),l.createElement("ul",{className:(0,n.Z)(u,"clean-list")},t.items.map((e=>l.createElement("li",{key:e.permalink,className:d},l.createElement(s.Z,{isNavLink:!0,to:e.permalink,className:g,activeClassName:p},e.title)))))))}var E=a(7548);function f(e){let{sidebar:t}=e;return l.createElement("ul",{className:"menu__list"},t.items.map((e=>l.createElement("li",{key:e.permalink,className:"menu__list-item"},l.createElement(s.Z,{isNavLink:!0,to:e.permalink,className:"menu__link",activeClassName:"menu__link--active"},e.title)))))}function v(e){return l.createElement(E.Zo,{component:f,props:e})}function b(e){let{sidebar:t}=e;const a=(0,o.i)();return null!=t&&t.items.length?"mobile"===a?l.createElement(v,{sidebar:t}):l.createElement(h,{sidebar:t}):null}function N(e){const{sidebar:t,toc:a,children:o,...s}=e,c=t&&t.items.length>0;return l.createElement(r.Z,s,l.createElement("div",{className:"container margin-vert--lg"},l.createElement("div",{className:"row"},l.createElement(b,{sidebar:t}),l.createElement("main",{className:(0,n.Z)("col",{"col--7":c,"col--9 col--offset-1":!c}),itemScope:!0,itemType:"http://schema.org/Blog"},o),a&&l.createElement("div",{className:"col col--2"},a))))}},4646:(e,t,a)=>{a.d(t,{Z:()=>L});var l=a(2784),n=a(6277),r=a(1375),o=a(77);function s(e){var t;let{children:a,className:n}=e;const{frontMatter:s,assets:c}=(0,r.C)(),{withBaseUrl:i}=(0,o.C)(),m=null!=(t=c.image)?t:s.image;return l.createElement("article",{className:n,itemProp:"blogPost",itemScope:!0,itemType:"http://schema.org/BlogPosting"},m&&l.createElement("meta",{itemProp:"image",content:i(m,{absolute:!0})}),a)}var c=a(9817);const i="title_cIQJ";function m(e){let{className:t}=e;const{metadata:a,isBlogPostPage:o}=(0,r.C)(),{permalink:s,title:m}=a,u=o?"h1":"h2";return l.createElement(u,{className:(0,n.Z)(i,t),itemProp:"headline"},o?m:l.createElement(c.Z,{itemProp:"url",to:s},m))}var u=a(1077),d=a(7239);const g="container_PuMg";function p(e){let{readingTime:t}=e;const a=function(){const{selectMessage:e}=(0,d.c)();return t=>{const a=Math.ceil(t);return e(a,(0,u.I)({id:"theme.blog.post.readingTime.plurals",description:'Pluralized label for "{readingTime} min read". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',message:"One min read|{readingTime} min read"},{readingTime:a}))}}();return l.createElement(l.Fragment,null,a(t))}function h(e){let{date:t,formattedDate:a}=e;return l.createElement("time",{dateTime:t,itemProp:"datePublished"},a)}function E(){return l.createElement(l.Fragment,null," \xb7 ")}function f(e){let{className:t}=e;const{metadata:a}=(0,r.C)(),{date:o,formattedDate:s,readingTime:c}=a;return l.createElement("div",{className:(0,n.Z)(g,"margin-vert--md",t)},l.createElement(h,{date:o,formattedDate:s}),void 0!==c&&l.createElement(l.Fragment,null,l.createElement(E,null),l.createElement(p,{readingTime:c})))}function v(e){return e.href?l.createElement(c.Z,e):l.createElement(l.Fragment,null,e.children)}function b(e){let{author:t,className:a}=e;const{name:r,title:o,url:s,imageURL:c,email:i}=t,m=s||i&&"mailto:"+i||void 0;return l.createElement("div",{className:(0,n.Z)("avatar margin-bottom--sm",a)},c&&l.createElement(v,{href:m,className:"avatar__photo-link"},l.createElement("img",{className:"avatar__photo",src:c,alt:r})),r&&l.createElement("div",{className:"avatar__intro",itemProp:"author",itemScope:!0,itemType:"https://schema.org/Person"},l.createElement("div",{className:"avatar__name"},l.createElement(v,{href:m,itemProp:"url"},l.createElement("span",{itemProp:"name"},r))),o&&l.createElement("small",{className:"avatar__subtitle",itemProp:"description"},o)))}const N="authorCol_q_iI",P="imageOnlyAuthorRow_les7",_="imageOnlyAuthorCol_uMKf";function Z(e){let{className:t}=e;const{metadata:{authors:a},assets:o}=(0,r.C)();if(0===a.length)return null;const s=a.every((e=>{let{name:t}=e;return!t}));return l.createElement("div",{className:(0,n.Z)("margin-top--md margin-bottom--sm",s?P:"row",t)},a.map(((e,t)=>{var a;return l.createElement("div",{className:(0,n.Z)(!s&&"col col--6",s?_:N),key:t},l.createElement(b,{author:{...e,imageURL:null!=(a=o.authorsImageUrls[t])?a:e.imageURL}}))})))}function k(){return l.createElement("header",null,l.createElement(m,null),l.createElement(f,null),l.createElement(Z,null))}var w=a(958),C=a(8632);function T(e){let{children:t,className:a}=e;const{isBlogPostPage:o}=(0,r.C)();return l.createElement("div",{id:o?w.blogPostContainerID:void 0,className:(0,n.Z)("markdown",a),itemProp:"articleBody"},l.createElement(C.Z,null,t))}var M=a(5040),I=a(2569),y=a(7896);function B(){return l.createElement("b",null,l.createElement(u.Z,{id:"theme.blog.post.readMore",description:"The label used in blog post item excerpts to link to full blog posts"},"Read More"))}function F(e){const{blogPostTitle:t,...a}=e;return l.createElement(c.Z,(0,y.Z)({"aria-label":(0,u.I)({message:"Read more about {title}",id:"theme.blog.post.readMoreLabel",description:"The ARIA label for the link to full blog posts from excerpts"},{title:t})},a),l.createElement(B,null))}const R="blogPostFooterDetailsFull_bikM";function x(){const{metadata:e,isBlogPostPage:t}=(0,r.C)(),{tags:a,title:o,editUrl:s,hasTruncateMarker:c}=e,i=!t&&c,m=a.length>0;return m||i||s?l.createElement("footer",{className:(0,n.Z)("row docusaurus-mt-lg",t&&R)},m&&l.createElement("div",{className:(0,n.Z)("col",{"col--9":i})},l.createElement(I.Z,{tags:a})),t&&s&&l.createElement("div",{className:"col margin-top--sm"},l.createElement(M.Z,{editUrl:s})),i&&l.createElement("div",{className:(0,n.Z)("col text--right",{"col--3":m})},l.createElement(F,{blogPostTitle:o,to:e.permalink}))):null}function L(e){let{children:t,className:a}=e;const o=function(){const{isBlogPostPage:e}=(0,r.C)();return e?void 0:"margin-bottom--xl"}();return l.createElement(s,{className:(0,n.Z)(o,a)},l.createElement(k,null),l.createElement(T,null,t),l.createElement(x,null))}},1375:(e,t,a)=>{a.d(t,{C:()=>s,n:()=>o});var l=a(2784),n=a(6335);const r=l.createContext(null);function o(e){let{children:t,content:a,isBlogPostPage:n=!1}=e;const o=function(e){let{content:t,isBlogPostPage:a}=e;return(0,l.useMemo)((()=>({metadata:t.metadata,frontMatter:t.frontMatter,assets:t.assets,toc:t.toc,isBlogPostPage:a})),[t,a])}({content:a,isBlogPostPage:n});return l.createElement(r.Provider,{value:o},t)}function s(){const e=(0,l.useContext)(r);if(null===e)throw new n.i6("BlogPostProvider");return e}},7239:(e,t,a)=>{a.d(t,{c:()=>i});var l=a(2784),n=a(7614);const r=["zero","one","two","few","many","other"];function o(e){return r.filter((t=>e.includes(t)))}const s={locale:"en",pluralForms:o(["one","other"]),select:e=>1===e?"one":"other"};function c(){const{i18n:{currentLocale:e}}=(0,n.Z)();return(0,l.useMemo)((()=>{try{return function(e){const t=new Intl.PluralRules(e);return{locale:e,pluralForms:o(t.resolvedOptions().pluralCategories),select:e=>t.select(e)}}(e)}catch(t){return console.error('Failed to use Intl.PluralRules for locale "'+e+'".\nDocusaurus will fallback to the default (English) implementation.\nError: '+t.message+"\n"),s}}),[e])}function i(){const e=c();return{selectMessage:(t,a)=>function(e,t,a){const l=e.split("|");if(1===l.length)return l[0];l.length>a.pluralForms.length&&console.error("For locale="+a.locale+", a maximum of "+a.pluralForms.length+" plural forms are expected ("+a.pluralForms.join(",")+"), but the message contains "+l.length+": "+e);const n=a.select(t),r=a.pluralForms.indexOf(n);return l[Math.min(r,l.length-1)]}(a,t,e)}}}}]); \ No newline at end of file diff --git a/docs/assets/js/64a3b801.bc70b5ca.js b/docs/assets/js/64a3b801.bc70b5ca.js deleted file mode 100644 index bf670173..00000000 --- a/docs/assets/js/64a3b801.bc70b5ca.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1894],{5745:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-pages","id":"default"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/66406991.6557cce3.js b/docs/assets/js/66406991.6557cce3.js deleted file mode 100644 index 83d614f1..00000000 --- a/docs/assets/js/66406991.6557cce3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[110],{711:e=>{e.exports=JSON.parse('{"permalink":"/blog/tags/hello","page":1,"postsPerPage":10,"totalPages":1,"totalCount":2,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/66ff5741.c2237126.js b/docs/assets/js/66ff5741.c2237126.js deleted file mode 100644 index bfcc6544..00000000 --- a/docs/assets/js/66ff5741.c2237126.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5539],{876:(e,t,n)=>{n.d(t,{Zo:()=>h,kt:()=>k});var a=n(2784);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){o(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},i=Object.keys(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a<i.length;a++)n=i[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),p=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},h=function(e){var t=p(e.components);return a.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,h=s(e,["components","mdxType","originalType","parentName"]),c=p(n),k=o,m=c["".concat(l,".").concat(k)]||c[k]||d[k]||i;return n?a.createElement(m,r(r({ref:t},h),{},{components:n})):a.createElement(m,r({ref:t},h))}));function k(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,r[1]=s;for(var p=2;p<i;p++)r[p]=n[p];return a.createElement.apply(null,r)}return a.createElement.apply(null,n)}c.displayName="MDXCreateElement"},6075:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>p});var a=n(7896),o=(n(2784),n(876));const i={sidebar_position:3},r="Forms & Changesets Example",s={unversionedId:"forms-and-changesets/use-with-forms",id:"forms-and-changesets/use-with-forms",title:"Forms & Changesets Example",description:"Now that we've revisited Form Events and learned about Changesets, let's take a look at how we can use them together to",source:"@site/docs/07-forms-and-changesets/use-with-forms.md",sourceDirName:"07-forms-and-changesets",slug:"/forms-and-changesets/use-with-forms",permalink:"/docs/forms-and-changesets/use-with-forms",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Changesets",permalink:"/docs/forms-and-changesets/changesets"},next:{title:"Uploading Files",permalink:"/docs/category/uploading-files"}},l={},p=[{value:"Example LiveView",id:"example-liveview",level:2},{value:"Code Walk Through",id:"code-walk-through",level:2},{value:"First defining the Zod Schema, Types, and Changeset Factory",id:"first-defining-the-zod-schema-types-and-changeset-factory",level:3},{value:"Define the LiveView TContext and TEvent types",id:"define-the-liveview-tcontext-and-tevent-types",level:3},{value:"Use Helpers in <code>render</code>",id:"use-helpers-in-render",level:3},{value:"Handle <code>validate</code> event",id:"handle-validate-event",level:3},{value:"Handle <code>save</code> event",id:"handle-save-event",level:3},{value:"Bonus <code>toggle_checkout</code> event",id:"bonus-toggle_checkout-event",level:2},{value:"Conclusion",id:"conclusion",level:2}],h={toc:p};function d(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,a.Z)({},h,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"forms--changesets-example"},"Forms & Changesets Example"),(0,o.kt)("p",null,"Now that we've revisited Form Events and learned about Changesets, let's take a look at how we can use them together to\nbuild, validate, and process a form."),(0,o.kt)("h2",{id:"example-liveview"},"Example LiveView"),(0,o.kt)("p",null,'We\'re going to build a LiveView for managing our book library. We\'ll be able to add new books and mark those books as\n"available" or "checked out".'),(0,o.kt)("p",null,"Here's the LiveView example code:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'import {\n createLiveView,\n error_tag,\n form_for,\n html,\n LiveViewChangeset,\n newChangesetFactory,\n submit,\n text_input,\n} from "liveviewjs";\nimport { nanoid } from "nanoid";\nimport { z } from "zod";\n\n// Create the zod BookSchema\nconst BookSchema = z.object({\n id: z.string().default(nanoid),\n name: z.string().min(2).max(100),\n author: z.string().min(2).max(100),\n checked_out: z.boolean().default(false),\n});\n\n// Infer the Book type from the BookSchema\ntype Book = z.infer<typeof BookSchema>;\n\n// in memory data store for Books\nconst booksDB: Record<string, Book> = {};\n\n// Book LiveViewChangesetFactory\nconst bookCSF = newChangesetFactory<Book>(BookSchema);\n\nexport const booksLiveView = createLiveView<\n // Define the Context of the LiveView\n {\n books: Book[];\n changeset: LiveViewChangeset<Book>;\n },\n // Define events that are initiated by the end-user\n | { type: "save"; name: string; author: string }\n | { type: "validate"; name: string; author: string }\n | { type: "toggle-checkout"; id: string }\n>({\n mount: async (socket) => {\n socket.assign({\n books: Object.values(booksDB),\n changeset: bookCSF({}, {}), // empty changeset\n });\n },\n\n handleEvent: (event, socket) => {\n switch (event.type) {\n case "validate":\n // validate the form data\n socket.assign({\n changeset: bookCSF({}, event, "validate"),\n });\n break;\n case "save":\n // attempt to create the volunteer from the form data\n const saveChangeset = bookCSF({}, event, "save");\n let changeset = saveChangeset;\n if (saveChangeset.valid) {\n // save the book to the in memory data store\n const newBook = saveChangeset.data as Book;\n booksDB[newBook.id] = newBook;\n // since book was saved, reset the changeset to empty\n changeset = bookCSF({}, {});\n }\n // update context\n socket.assign({\n books: Object.values(booksDB),\n changeset,\n });\n break;\n case "toggle-checkout":\n // lookup book by id\n const book = booksDB[event.id];\n if (book) {\n // update book\n book.checked_out = !book.checked_out;\n booksDB[book.id] = book;\n // update context\n socket.assign({\n books: Object.values(booksDB),\n });\n }\n break;\n }\n },\n\n render: (context, meta) => {\n const { changeset, books } = context;\n const { csrfToken } = meta;\n return html`\n <h1>My Library</h1>\n\n <div id="bookForm">\n ${form_for<Book>("#", csrfToken, {\n phx_submit: "save",\n phx_change: "validate",\n })}\n\n <div class="field">\n ${text_input<Book>(changeset, "name", { placeholder: "Name", autocomplete: "off", phx_debounce: 1000 })}\n ${error_tag(changeset, "name")}\n </div>\n\n <div class="field">\n ${text_input<Book>(changeset, "author", { placeholder: "Author", autocomplete: "off", phx_debounce: 1000 })}\n ${error_tag(changeset, "author")}\n </div>\n\n ${submit("Add Book", { phx_disable_with: "Saving..." })}\n </form>\n </div>\n\n <div id="books">\n ${books.map(renderBook)}\n </div>\n `;\n },\n});\n\nfunction renderBook(b: Book) {\n const color = b.checked_out ? `color: #ccc;` : ``;\n const emoji = b.checked_out ? `\ud83d\udcd6[checked out]` : `\ud83d\udcda[available]`;\n return html`\n <div id="${b.id}" style="margin-top: 1rem; ${color}">\n ${emoji}<span>${b.name}</span> by <span>${b.author}</span>\n <div>\n <button phx-click="toggle-checkout" phx-value-id="${b.id}" phx-disable-with="Updating...">\n ${b.checked_out ? "Return" : "Check Out"}\n </button>\n </div>\n </div>\n `;\n}\n')),(0,o.kt)("h2",{id:"code-walk-through"},"Code Walk Through"),(0,o.kt)("p",null,"Let's walk through the code to see how it all works together."),(0,o.kt)("h3",{id:"first-defining-the-zod-schema-types-and-changeset-factory"},"First defining the Zod Schema, Types, and Changeset Factory"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},"// Create the zod BookSchema\nconst BookSchema = z.object({\n id: z.string().default(nanoid),\n name: z.string().min(2).max(100),\n author: z.string().min(2).max(100),\n checked_out: z.boolean().default(false),\n});\n\n// Infer the Book type from the BookSchema\ntype Book = z.infer<typeof BookSchema>;\n\n// Book LiveViewChangesetFactory\nconst bookCSF = newChangesetFactory<Book>(BookSchema);\n")),(0,o.kt)("p",null,"This code should look familiar. We're defining the Zod Schema, inferring the type, and creating the changeset factory\nfor the ",(0,o.kt)("inlineCode",{parentName:"p"},"BookSchema")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"Book")," type."),(0,o.kt)("h3",{id:"define-the-liveview-tcontext-and-tevent-types"},"Define the LiveView TContext and TEvent types"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'...\nexport const booksLiveView = createLiveView<\n // Define the Context of the LiveView\n {\n books: Book[];\n changeset: LiveViewChangeset<Book>;\n },\n // Define events that are initiated by the end-user\n | { type: "save"; name: string; author: string }\n | { type: "validate"; name: string; author: string }\n | { type: "toggle-checkout"; id: string }\n>({\n...\n')),(0,o.kt)("p",null,"Here we are saying the ",(0,o.kt)("inlineCode",{parentName:"p"},"context")," of our LiveView will have a ",(0,o.kt)("inlineCode",{parentName:"p"},"books")," array of type ",(0,o.kt)("inlineCode",{parentName:"p"},"Book")," and a ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," of type\n",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset<Book>"),". We are also defining the ",(0,o.kt)("inlineCode",{parentName:"p"},"events")," that can be initiated by the end-user. In this case, we\nhave ",(0,o.kt)("inlineCode",{parentName:"p"},"save"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"validate"),", and ",(0,o.kt)("inlineCode",{parentName:"p"},"toggle-checkout")," events along with the data that is required for each event."),(0,o.kt)("h3",{id:"use-helpers-in-render"},"Use Helpers in ",(0,o.kt)("inlineCode",{parentName:"h3"},"render")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'...\nrender: (context, meta) => {\n // get the changeset and books from the context\n const { changeset, books } = context;\n // pull out the csrf token from the meta data\n const { csrfToken } = meta;\n return html`\n <h1>My Library</h1>\n\n <div id="bookForm">\n \x3c!-- use the form_for helper to render the form --\x3e\n ${form_for<Book>("#", csrfToken, {\n phx_submit: "save",\n phx_change: "validate",\n })}\n\n <div class="field">\n \x3c!-- use the text_input helper to render the input for the name property --\x3e\n ${text_input<Book>(changeset, "name", { placeholder: "Name", autocomplete: "off", phx_debounce: 1000 })}\n \x3c!-- use the error_tag helper to optionally render the error message for the name property --\x3e\n ${error_tag(changeset, "name")}\n </div>\n\n <div class="field">\n \x3c!-- use the text_input helper to render the input for the author property --\x3e\n ${text_input<Book>(changeset, "author", { placeholder: "Author", autocomplete: "off", phx_debounce: 1000 })}\n \x3c!-- use the error_tag helper to optionally render the error message for the author property --\x3e\n ${error_tag(changeset, "author")}\n </div>\n \x3c!-- use the submit helper to render the submit button --\x3e\n ${submit("Add Book", { phx_disable_with: "Saving..." })}\n </form>\n\n </div>\n\n <div id="books">\n ${books.map(renderBook)}\n </div>\n `;\n},\n...\n')),(0,o.kt)("p",null,"There is a bit going on here so let's break it down. First we are pulling out the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"books")," from the\n",(0,o.kt)("inlineCode",{parentName:"p"},"context"),". We are also pulling out the ",(0,o.kt)("inlineCode",{parentName:"p"},"csrfToken")," from the ",(0,o.kt)("inlineCode",{parentName:"p"},"meta")," data. Next we are using the ",(0,o.kt)("inlineCode",{parentName:"p"},"form_for")," helper to\nrender the form. We are passing in the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," and the ",(0,o.kt)("inlineCode",{parentName:"p"},"csrfToken")," as well as setting the ",(0,o.kt)("inlineCode",{parentName:"p"},"phx_submit")," and\n",(0,o.kt)("inlineCode",{parentName:"p"},"phx_change")," attributes to the events that will be called in ",(0,o.kt)("inlineCode",{parentName:"p"},"handleEvent"),"."),(0,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"Using the ",(0,o.kt)("inlineCode",{parentName:"p"},"form_for")," helper is optional. You can use the ",(0,o.kt)("inlineCode",{parentName:"p"},"form")," helper and set the ",(0,o.kt)("inlineCode",{parentName:"p"},"phx_submit")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"phx_change"),"\nattributes to the events that will be called in ",(0,o.kt)("inlineCode",{parentName:"p"},"handleEvent"),". The ",(0,o.kt)("inlineCode",{parentName:"p"},"form_for")," helper just makes it a little easier to\nrender the form and automatically passes the ",(0,o.kt)("inlineCode",{parentName:"p"},"csrfToken")," to the form."),(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," framework automatically validates the csrfToken (a.k.a. authenticity token) for you and will throw an\nerror if the token is invalid.")),(0,o.kt)("p",null,"Next we are using the ",(0,o.kt)("inlineCode",{parentName:"p"},"text_input")," helper to render the input for the ",(0,o.kt)("inlineCode",{parentName:"p"},"name")," property. We are passing in the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset"),"\nand the ",(0,o.kt)("inlineCode",{parentName:"p"},"name")," property as well as the ",(0,o.kt)("inlineCode",{parentName:"p"},"placeholder"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"autocomplete"),", and ",(0,o.kt)("inlineCode",{parentName:"p"},"phx_debounce")," attributes."),(0,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("inlineCode",{parentName:"p"},"text_input")," helper is optional but it provides type safefy and automatically works with the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset"),". Of\nnote, we use the ",(0,o.kt)("inlineCode",{parentName:"p"},"phx-debounce")," attribute to only send the change event to the server after the user has stopped typing\nfor 1000ms. This is a great way to reduce the number of events sent to the server and improve performance.")),(0,o.kt)("p",null,"Next we are using the ",(0,o.kt)("inlineCode",{parentName:"p"},"error_tag")," helper to optionally render the error message for the ",(0,o.kt)("inlineCode",{parentName:"p"},"name")," property. We are passing\nin the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," and the ",(0,o.kt)("inlineCode",{parentName:"p"},"name")," property there as well."),(0,o.kt)("admonition",{type:"tip"},(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("inlineCode",{parentName:"p"},"error_tag")," helper is optional but it provides type safefy and automatically works with the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," to\npull out the error message for the property if there is one.")),(0,o.kt)("p",null,"We follow the same pattern for the ",(0,o.kt)("inlineCode",{parentName:"p"},"author")," property."),(0,o.kt)("p",null,"Next, we are using the ",(0,o.kt)("inlineCode",{parentName:"p"},"submit")," helper to render the submit button. We are passing in the ",(0,o.kt)("inlineCode",{parentName:"p"},"Add Book")," text and the\n",(0,o.kt)("inlineCode",{parentName:"p"},"phx_disable_with")," attribute."),(0,o.kt)("p",null,"Finally we map over the ",(0,o.kt)("inlineCode",{parentName:"p"},"books")," to render each book using the ",(0,o.kt)("inlineCode",{parentName:"p"},"renderBook")," function."),(0,o.kt)("h3",{id:"handle-validate-event"},"Handle ",(0,o.kt)("inlineCode",{parentName:"h3"},"validate")," event"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'handleEvent: (event, socket) => {\n...\ncase "validate":\n // validate the form data\n socket.assign({\n changeset: bookCSF({}, event, "validate"),\n });\n break;\n...\n')),(0,o.kt)("p",null,"When ",(0,o.kt)("inlineCode",{parentName:"p"},"handleEvent")," occurs with the ",(0,o.kt)("inlineCode",{parentName:"p"},"validate")," event, we use the ",(0,o.kt)("inlineCode",{parentName:"p"},"bookCSF")," changeset factory to generate a new\n",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for the the form data (also present in the ",(0,o.kt)("inlineCode",{parentName:"p"},"event"),"). Since, this isn't updating the book, we pass an\nempty object ",(0,o.kt)("inlineCode",{parentName:"p"},"{}")," first, along with the ",(0,o.kt)("inlineCode",{parentName:"p"},"event")," data. Importantly, we set the ",(0,o.kt)("inlineCode",{parentName:"p"},"action")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"validate")," and assign the\nresult to the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," in the ",(0,o.kt)("inlineCode",{parentName:"p"},"context"),"."),(0,o.kt)("admonition",{type:"info"},(0,o.kt)("p",{parentName:"admonition"},"Remember if you don't set the ",(0,o.kt)("inlineCode",{parentName:"p"},"action")," text in the ",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewChangesetFactory")," call then returned\n",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," will always be valid.")),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," works with the helpers in ",(0,o.kt)("inlineCode",{parentName:"p"},"render")," to automatically render the error messages for the properties\nthat have errors and set the value of the input fields to the values that were submitted."),(0,o.kt)("h3",{id:"handle-save-event"},"Handle ",(0,o.kt)("inlineCode",{parentName:"h3"},"save")," event"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'handleEvent: (event, socket) => {\n...\ncase "save":\n // attempt to create the volunteer from the form data\n const saveChangeset = bookCSF({}, event, "save");\n let changeset = saveChangeset;\n if (saveChangeset.valid) {\n // save the book to the in memory data store\n const newBook = saveChangeset.data as Book;\n booksDB[newBook.id] = newBook;\n // since book was saved, reset the changeset to empty\n changeset = bookCSF({}, {});\n }\n // update context\n socket.assign({\n books: Object.values(booksDB),\n changeset,\n });\n break;\n...\n')),(0,o.kt)("p",null,"When ",(0,o.kt)("inlineCode",{parentName:"p"},"handleEvent")," occurs with the ",(0,o.kt)("inlineCode",{parentName:"p"},"save")," event, we use the ",(0,o.kt)("inlineCode",{parentName:"p"},"bookCSF")," changeset factory to generate a new\n",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewChangeset")," for the the form data (also present in the ",(0,o.kt)("inlineCode",{parentName:"p"},"event"),"). Since, this is also not updating an existing\nbook, we pass an empty object ",(0,o.kt)("inlineCode",{parentName:"p"},"{}")," first, along with the ",(0,o.kt)("inlineCode",{parentName:"p"},"event")," data. Importantly, we set the ",(0,o.kt)("inlineCode",{parentName:"p"},"action")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"save")," and\nassign the result to the ",(0,o.kt)("inlineCode",{parentName:"p"},"saveChangeset")," variable."),(0,o.kt)("p",null,"If the ",(0,o.kt)("inlineCode",{parentName:"p"},"saveChangeset")," is valid, we save the book to the in memory data store. We then reset the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," to be an\nempty changeset (i.e., ",(0,o.kt)("inlineCode",{parentName:"p"},"bookCSF({}, {})"),"). If the ",(0,o.kt)("inlineCode",{parentName:"p"},"saveChangeset")," is not valid, we keep the ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," as the\n",(0,o.kt)("inlineCode",{parentName:"p"},"saveChangeset")," so that the error messages will be rendered in the form using the form helpers (i.e., ",(0,o.kt)("inlineCode",{parentName:"p"},"error_tag")," and\n",(0,o.kt)("inlineCode",{parentName:"p"},"text_input"),")."),(0,o.kt)("p",null,"Finally, we update the ",(0,o.kt)("inlineCode",{parentName:"p"},"books")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"changeset")," in the ",(0,o.kt)("inlineCode",{parentName:"p"},"context"),"."),(0,o.kt)("h2",{id:"bonus-toggle_checkout-event"},"Bonus ",(0,o.kt)("inlineCode",{parentName:"h2"},"toggle_checkout")," event"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'handleEvent: (event, socket) => {\n...\ncase "toggle_checkout":\n // lookup book by id\n const book = booksDB[event.id];\n if (book) {\n // update book\n book.checked_out = !book.checked_out;\n booksDB[book.id] = book;\n // update context\n socket.assign({\n books: Object.values(booksDB),\n });\n }\n break;\n...\n')),(0,o.kt)("p",null,"When ",(0,o.kt)("inlineCode",{parentName:"p"},"handleEvent")," occurs with the ",(0,o.kt)("inlineCode",{parentName:"p"},"toggle_checkout")," event, we toggle the ",(0,o.kt)("inlineCode",{parentName:"p"},"checked_out")," property of the book in the in\nmemory data store and update the ",(0,o.kt)("inlineCode",{parentName:"p"},"books")," in the ",(0,o.kt)("inlineCode",{parentName:"p"},"context"),"."),(0,o.kt)("h2",{id:"conclusion"},"Conclusion"),(0,o.kt)("p",null,"As you can see, Forms and Changesets are a powerful way to build forms in LiveView. They provide type safety and\nautomatically render the error messages and input values. They validate the form data incrementally and upon submission.\nIn short, they make forms easy and fun again!"))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/6875c492.5c47c8f7.js b/docs/assets/js/6875c492.5c47c8f7.js deleted file mode 100644 index 2583e3f1..00000000 --- a/docs/assets/js/6875c492.5c47c8f7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8610],{2156:(e,t,a)=>{a.d(t,{Z:()=>s});var n=a(2784),l=a(1077),r=a(7066);function s(e){const{metadata:t}=e,{previousPage:a,nextPage:s}=t;return n.createElement("nav",{className:"pagination-nav","aria-label":(0,l.I)({id:"theme.blog.paginator.navAriaLabel",message:"Blog list page navigation",description:"The ARIA label for the blog pagination"})},a&&n.createElement(r.Z,{permalink:a,title:n.createElement(l.Z,{id:"theme.blog.paginator.newerEntries",description:"The label used to navigate to the newer blog posts page (previous page)"},"Newer Entries")}),s&&n.createElement(r.Z,{permalink:s,title:n.createElement(l.Z,{id:"theme.blog.paginator.olderEntries",description:"The label used to navigate to the older blog posts page (next page)"},"Older Entries"),isNext:!0}))}},1852:(e,t,a)=>{a.d(t,{Z:()=>s});var n=a(2784),l=a(1375),r=a(4646);function s(e){let{items:t,component:a=r.Z}=e;return n.createElement(n.Fragment,null,t.map((e=>{let{content:t}=e;return n.createElement(l.n,{key:t.metadata.permalink,content:t},n.createElement(a,null,n.createElement(t,null)))})))}},242:(e,t,a)=>{a.r(t),a.d(t,{default:()=>E});var n=a(2784),l=a(6277),r=a(1077),s=a(7239),o=a(328),i=a(211),g=a(9817),c=a(3485),m=a(2156),p=a(4390),u=a(1852);function d(e){const t=function(){const{selectMessage:e}=(0,s.c)();return t=>e(t,(0,r.I)({id:"theme.blog.post.plurals",description:'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',message:"One post|{count} posts"},{count:t}))}();return(0,r.I)({id:"theme.blog.tagTitle",description:"The title of the page for a blog tag",message:'{nPosts} tagged with "{tagName}"'},{nPosts:t(e.count),tagName:e.label})}function h(e){let{tag:t}=e;const a=d(t);return n.createElement(n.Fragment,null,n.createElement(o.d,{title:a}),n.createElement(p.Z,{tag:"blog_tags_posts"}))}function b(e){let{tag:t,items:a,sidebar:l,listMetadata:s}=e;const o=d(t);return n.createElement(c.Z,{sidebar:l},n.createElement("header",{className:"margin-bottom--xl"},n.createElement("h1",null,o),n.createElement(g.Z,{href:t.allTagsPath},n.createElement(r.Z,{id:"theme.tags.tagsPageLink",description:"The label of the link targeting the tag list page"},"View All Tags"))),n.createElement(u.Z,{items:a}),n.createElement(m.Z,{metadata:s}))}function E(e){return n.createElement(o.FG,{className:(0,l.Z)(i.k.wrapper.blogPages,i.k.page.blogTagPostListPage)},n.createElement(h,e),n.createElement(b,e))}}}]); \ No newline at end of file diff --git a/docs/assets/js/6bf4f685.9876148d.js b/docs/assets/js/6bf4f685.9876148d.js deleted file mode 100644 index 30f3cbda..00000000 --- a/docs/assets/js/6bf4f685.9876148d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2294],{876:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>b});var i=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},o=Object.keys(e);for(i=0;i<o.length;i++)n=o[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i<o.length;i++)n=o[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),p=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=p(n),b=a,d=m["".concat(l,".").concat(b)]||m[b]||c[b]||o;return n?i.createElement(d,r(r({ref:t},u),{},{components:n})):i.createElement(d,r({ref:t},u))}));function b(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,r[1]=s;for(var p=2;p<o;p++)r[p]=n[p];return i.createElement.apply(null,r)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},7709:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var i=n(7896),a=(n(2784),n(876));const o={sidebar_position:1},r="Overview",s={unversionedId:"real-time-multi-player-pub-sub/overview",id:"real-time-multi-player-pub-sub/overview",title:"Overview",description:"LiveViewJS natively supports real-time, multi-player user experiences. This is because LiveViewJS (and Phoenix",source:"@site/docs/09-real-time-multi-player-pub-sub/overview.md",sourceDirName:"09-real-time-multi-player-pub-sub",slug:"/real-time-multi-player-pub-sub/overview",permalink:"/docs/real-time-multi-player-pub-sub/overview",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Real-time / Multi-player",permalink:"/docs/category/real-time--multi-player"},next:{title:"Example Pub/Sub LiveView",permalink:"/docs/real-time-multi-player-pub-sub/example-pub-sub"}},l={},p=[{value:"Pub/Sub in LiveViewJS",id:"pubsub-in-liveviewjs",level:2},{value:"Subscribe to Topics",id:"subscribe-to-topics",level:2},{value:"Broadcast to Topics",id:"broadcast-to-topics",level:2},{value:"Connecting with other LiveViews",id:"connecting-with-other-liveviews",level:2},{value:"Connecting with External Events",id:"connecting-with-external-events",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"overview"},"Overview"),(0,a.kt)("p",null,"LiveViewJS natively supports real-time, multi-player user experiences. This is because ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," (and Phoenix\nLiveView for that matter) are built on top of Pub/Sub primatives."),(0,a.kt)("p",null,"Pub/Sub is a common pattern for decoupling processes by allowing messages to be sent to a topic by one process and\nreceived asynchronously by another. In LiveViewJS, pub/sub is used to enable real-time, multi-player web application."),(0,a.kt)("h2",{id:"pubsub-in-liveviewjs"},"Pub/Sub in LiveViewJS"),(0,a.kt)("p",null,"There are two main ways to enable real-time, multi-player experiences in LiveViewJS:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"subscribe to topics")," - If you have a LiveView that needs to receive messages from a topic, you can subscribe to the\ntopic in the ",(0,a.kt)("inlineCode",{parentName:"li"},"mount"),' method and receive "Info" messages in the ',(0,a.kt)("inlineCode",{parentName:"li"},"handleInfo")," method."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"broadcast to topics")," - If you have a LiveView (or data model) that needs to send messages to a topic, you can\nbroadcast to the topic using an implementation of the ",(0,a.kt)("inlineCode",{parentName:"li"},"PubSub")," interface.")),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," ships with three implementations of the ",(0,a.kt)("inlineCode",{parentName:"p"},"PubSub")," interface:"),(0,a.kt)("ul",{parentName:"admonition"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"SingleProcessPubSub")," - A pub/sub implementation (backed by ",(0,a.kt)("inlineCode",{parentName:"li"},"EventEmitter")," that is useful for testing and development\n(as it only works in a single process)."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"RedisPubSub")," - A pub/sub implementation that uses Redis as the pub/sub backend. This is useful for production\ndeployments that are running on NodeJS"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BroadcastChannelPubSub")," - A pub/sub implementation that uses the ",(0,a.kt)("inlineCode",{parentName:"li"},"BroadcastChannel")," API which is a server enabled\npub/sub implementation that is useful for production deployments on Deno Deploy."))),(0,a.kt)("h2",{id:"subscribe-to-topics"},"Subscribe to Topics"),(0,a.kt)("p",null,"If you have a LiveView that needs to receive messages from a topic, you can subscribe to the topic in the ",(0,a.kt)("inlineCode",{parentName:"p"},"mount"),' method\nand receive "Info" messages in the ',(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo")," method."),(0,a.kt)("p",null,"For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nmount: (socket) => {\n // subscribe to Info events on the "my_topic" topic\n socket.subscribe("my_topic");\n},\n...\n')),(0,a.kt)("p",null,"This will cause the ",(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo"),' method to be called with any messages that are broadcast to the "my_topic" topic.'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nhandleInfo: (info, socket) => {\n // handle info messages\n switch (info.type) {\n ...\n case "my_topic":\n // handle my_topic messages\n break;\n ...\n }\n},\n...\n')),(0,a.kt)("h2",{id:"broadcast-to-topics"},"Broadcast to Topics"),(0,a.kt)("p",null,"If you have a LiveView (or data model) that needs to send messages to a topic, you can broadcast to the topic using an\nimplementation of the ",(0,a.kt)("inlineCode",{parentName:"p"},"PubSub")," interface."),(0,a.kt)("p",null,"For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\n// create a pubsub instance\nconst pubSub = new SingleProcessPubSub();\n...\n// broadcast a message to the "my_topic" topic\npubSub.publish("my_topic", { message: "Hello World" });\n...\n')),(0,a.kt)("p",null,"This will cause the ",(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo"),' method to be called with any messages that are broadcast to the "my_topic" topic.'),(0,a.kt)("h2",{id:"connecting-with-other-liveviews"},"Connecting with other LiveViews"),(0,a.kt)("p",null,"It should be clear at this point that if you want to connect LiveView with a different LiveView (either the same type or\na different type) all you need to do is broadcast a message to a topic that the other LiveView is subscribed to."),(0,a.kt)("h2",{id:"connecting-with-external-events"},"Connecting with External Events"),(0,a.kt)("p",null,"Let's say you have an event that is happening outside of the LiveViewJS. All you have to do is connect that event with\nthe ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," by broadcasting a message to a topic that the ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," is subscribed to. This means you either\nneed to use the ",(0,a.kt)("inlineCode",{parentName:"p"},"PubSub")," implementation (with the same configuration) in that different code or you need to use the same\npub/sub backend (e.g., Redis or BroadcastChannel)."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/6c9f2a26.46cf69f9.js b/docs/assets/js/6c9f2a26.46cf69f9.js deleted file mode 100644 index 4a16ff60..00000000 --- a/docs/assets/js/6c9f2a26.46cf69f9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5071],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>u});var i=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},a=Object.keys(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i<a.length;i++)n=a[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=i.createContext({}),m=function(e){var t=i.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=m(e.components);return i.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=m(n),u=r,v=d["".concat(s,".").concat(u)]||d[u]||p[u]||a;return n?i.createElement(v,o(o({ref:t},c),{},{components:n})):i.createElement(v,o({ref:t},c))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var m=2;m<a;m++)o[m]=n[m];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}d.displayName="MDXCreateElement"},471:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>m});var i=n(7896),r=(n(2784),n(876));const a={sidebar_position:1},o="Overview",l={unversionedId:"js-commands/overview",id:"js-commands/overview",title:"Overview",description:"LiveViewJS user events (clicks, etc) typically trigger a server-side event which updates the state of the LiveView and",source:"@site/docs/11-js-commands/overview.md",sourceDirName:"11-js-commands",slug:"/js-commands/overview",permalink:"/docs/js-commands/overview",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"JS Commands",permalink:"/docs/category/js-commands"},next:{title:"Add/Remove Class Commands",permalink:"/docs/js-commands/add-remove-class"}},s={},m=[{value:"JS Command Syntax",id:"js-command-syntax",level:2},{value:""Chainable" (i.e., fluent) Syntax",id:"chainable-ie--fluent-syntax",level:2},{value:"Example JS Commands LiveView",id:"example-js-commands-liveview",level:2}],c={toc:m};function p(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"overview"},"Overview"),(0,r.kt)("p",null,"LiveViewJS user events (clicks, etc) typically trigger a server-side event which updates the state of the LiveView and\nre-renders the HTML. Sometimes you want to update the DOM without (or in addition to) initiating a server round trip.\nThis is where JS Commands come in."),(0,r.kt)("p",null,"JS Commands support a number of client-side DOM manipulation function that can be used to update the DOM without a\nserver round trip. These functions are:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"add_class")," - Add css classes to an element including optional transition classes"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"remove_class")," - Remove css classes from an element including optional transition classes"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"set_attribute")," - Set an attribute on an element"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"remove_attribute")," - Remove an attribute from an element"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"show")," - Show an element including optional transition classes"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"hide")," - Hide an element including optional transition classes"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"toggle")," - Toggle the visibility of an element"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"dispatch")," - Dispatch a DOM event from an element"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"transition")," - Apply transition classes to an element (i.e., animate it)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"push")," - Push an event to the LiveView server (i.e., trigger a server round trip)")),(0,r.kt)("h2",{id:"js-command-syntax"},"JS Command Syntax"),(0,r.kt)("p",null,"JS Commands are used in the ",(0,r.kt)("inlineCode",{parentName:"p"},"render")," function of a LiveView or LiveComponent:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-typescript"},'import { JS } from \'lieviewjs\';\n\n//... render function of a LiveView\nrender() {\n return html`\n <div>\n <button phx-click="${new JS().toggle({ to: "#toggle" })}">Toggle</button>\n <div id="toggle">Toggle Me</div>\n </div>\n `;\n}\n')),(0,r.kt)("h2",{id:"chainable-ie--fluent-syntax"},'"Chainable" (i.e., fluent) Syntax'),(0,r.kt)("p",null,'JS Commands are "chainable" (i.e., fluent) so you can chain multiple commands together as needed and they will be\nexecuted in the order they are called:'),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-typescript"},'import { JS } from \'lieviewjs\';\n\n//... render function of a LiveView\nrender() {\n // hide the button then push the "increment" event to the server\n return html`\n <div>\n <button phx-click="${new JS().hide().push("increment")}">Increment</button>\n </div>\n `;\n}\n')),(0,r.kt)("h2",{id:"example-js-commands-liveview"},"Example JS Commands LiveView"),(0,r.kt)("p",null,"See ",(0,r.kt)("inlineCode",{parentName:"p"},"packages/examples/src/liveviews/jsCommands")," for a working, complete set of examples of using JS Commands."),(0,r.kt)("p",null,"More details on each JS Command can be found in the following sections."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/73664a40.4ae7ea32.js b/docs/assets/js/73664a40.4ae7ea32.js deleted file mode 100644 index db69fc7d..00000000 --- a/docs/assets/js/73664a40.4ae7ea32.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3514],{876:(e,t,o)=>{o.d(t,{Zo:()=>a,kt:()=>d});var i=o(2784);function s(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function r(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,i)}return o}function n(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?r(Object(o),!0).forEach((function(t){s(e,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):r(Object(o)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))}))}return e}function l(e,t){if(null==e)return{};var o,i,s=function(e,t){if(null==e)return{};var o,i,s={},r=Object.keys(e);for(i=0;i<r.length;i++)o=r[i],t.indexOf(o)>=0||(s[o]=e[o]);return s}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)o=r[i],t.indexOf(o)>=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(s[o]=e[o])}return s}var u=i.createContext({}),m=function(e){var t=i.useContext(u),o=t;return e&&(o="function"==typeof e?e(t):n(n({},t),e)),o},a=function(e){var t=m(e.components);return i.createElement(u.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},p=i.forwardRef((function(e,t){var o=e.components,s=e.mdxType,r=e.originalType,u=e.parentName,a=l(e,["components","mdxType","originalType","parentName"]),p=m(o),d=s,g=p["".concat(u,".").concat(d)]||p[d]||c[d]||r;return o?i.createElement(g,n(n({ref:t},a),{},{components:o})):i.createElement(g,n({ref:t},a))}));function d(e,t){var o=arguments,s=t&&t.mdxType;if("string"==typeof e||s){var r=o.length,n=new Array(r);n[0]=p;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:s,n[1]=l;for(var m=2;m<r;m++)n[m]=o[m];return i.createElement.apply(null,n)}return i.createElement.apply(null,o)}p.displayName="MDXCreateElement"},1717:(e,t,o)=>{o.r(t),o.d(t,{assets:()=>u,contentTitle:()=>n,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>m});var i=o(7896),s=(o(2784),o(876));const r={slug:"long-blog-post",title:"Long Blog Post",authors:"floodfx",tags:["hello","docusaurus"]},n=void 0,l={permalink:"/blog/long-blog-post",source:"@site/blog/2019-05-29-long-blog-post.md",title:"Long Blog Post",description:"This is the summary of a very long blog post,",date:"2019-05-29T00:00:00.000Z",formattedDate:"May 29, 2019",tags:[{label:"hello",permalink:"/blog/tags/hello"},{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:2.05,hasTruncateMarker:!0,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"long-blog-post",title:"Long Blog Post",authors:"floodfx",tags:["hello","docusaurus"]},prevItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"},nextItem:{title:"First Blog Post",permalink:"/blog/first-blog-post"}},u={authorsImageUrls:[void 0]},m=[],a={toc:m};function c(e){let{components:t,...o}=e;return(0,s.kt)("wrapper",(0,i.Z)({},a,o,{components:t,mdxType:"MDXLayout"}),(0,s.kt)("p",null,"This is the summary of a very long blog post,"),(0,s.kt)("p",null,"Use a ",(0,s.kt)("inlineCode",{parentName:"p"},"\x3c!--")," ",(0,s.kt)("inlineCode",{parentName:"p"},"truncate")," ",(0,s.kt)("inlineCode",{parentName:"p"},"--\x3e")," comment to limit blog post size in the list view."),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"),(0,s.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/7661071f.5843a255.js b/docs/assets/js/7661071f.5843a255.js deleted file mode 100644 index 573bf26a..00000000 --- a/docs/assets/js/7661071f.5843a255.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9642],{876:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>m});var r=o(2784);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function l(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?a(Object(o),!0).forEach((function(t){n(e,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):a(Object(o)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))}))}return e}function u(e,t){if(null==e)return{};var o,r,n=function(e,t){if(null==e)return{};var o,r,n={},a=Object.keys(e);for(r=0;r<a.length;r++)o=a[r],t.indexOf(o)>=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)o=a[r],t.indexOf(o)>=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var i=r.createContext({}),s=function(e){var t=r.useContext(i),o=t;return e&&(o="function"==typeof e?e(t):l(l({},t),e)),o},c=function(e){var t=s(e.components);return r.createElement(i.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,a=e.originalType,i=e.parentName,c=u(e,["components","mdxType","originalType","parentName"]),f=s(o),m=n,d=f["".concat(i,".").concat(m)]||f[m]||p[m]||a;return o?r.createElement(d,l(l({ref:t},c),{},{components:o})):r.createElement(d,l({ref:t},c))}));function m(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var a=o.length,l=new Array(a);l[0]=f;var u={};for(var i in t)hasOwnProperty.call(t,i)&&(u[i]=t[i]);u.originalType=e,u.mdxType="string"==typeof e?e:n,l[1]=u;for(var s=2;s<a;s++)l[s]=o[s];return r.createElement.apply(null,l)}return r.createElement.apply(null,o)}f.displayName="MDXCreateElement"},2843:(e,t,o)=>{o.r(t),o.d(t,{assets:()=>i,contentTitle:()=>l,default:()=>p,frontMatter:()=>a,metadata:()=>u,toc:()=>s});var r=o(7896),n=(o(2784),o(876));const a={slug:"welcome",title:"Welcome",authors:["floodfx"],tags:["facebook","hello","docusaurus"]},l=void 0,u={permalink:"/blog/welcome",source:"@site/blog/2021-08-26-welcome/index.md",title:"Welcome",description:"Docusaurus blogging features are powered by the",date:"2021-08-26T00:00:00.000Z",formattedDate:"August 26, 2021",tags:[{label:"facebook",permalink:"/blog/tags/facebook"},{label:"hello",permalink:"/blog/tags/hello"},{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:.405,hasTruncateMarker:!1,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"welcome",title:"Welcome",authors:["floodfx"],tags:["facebook","hello","docusaurus"]},nextItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"}},i={authorsImageUrls:[void 0]},s=[],c={toc:s};function p(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/blog"},"Docusaurus blogging features")," are powered by the\n",(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog"},"blog plugin"),"."),(0,n.kt)("p",null,"Simply add Markdown files (or folders) to the ",(0,n.kt)("inlineCode",{parentName:"p"},"blog")," directory."),(0,n.kt)("p",null,"Regular blog authors can be added to ",(0,n.kt)("inlineCode",{parentName:"p"},"authors.yml"),"."),(0,n.kt)("p",null,"The blog post date can be extracted from filenames, such as:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"2019-05-30-welcome.md")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"2019-05-30-welcome/index.md"))),(0,n.kt)("p",null,"A blog post folder can be convenient to co-locate blog post images:"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Docusaurus Plushie",src:o(6070).Z,width:"1500",height:"500"})),(0,n.kt)("p",null,"The blog supports tags as well!"),(0,n.kt)("p",null,(0,n.kt)("strong",{parentName:"p"},"And if you don't want a blog"),": just delete this directory, and use ",(0,n.kt)("inlineCode",{parentName:"p"},"blog: false")," in your Docusaurus config."))}p.isMDXComponent=!0},6070:(e,t,o)=>{o.d(t,{Z:()=>r});const r=o.p+"assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg"}}]); \ No newline at end of file diff --git a/docs/assets/js/76864381.a33c114b.js b/docs/assets/js/76864381.a33c114b.js deleted file mode 100644 index 60ee2da8..00000000 --- a/docs/assets/js/76864381.a33c114b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2493],{876:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var o=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,o,r=function(e,t){if(null==e)return{};var n,o,r={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=o.createContext({}),p=function(e){var t=o.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=p(e.components);return o.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,c=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=p(n),d=r,h=m["".concat(c,".").concat(d)]||m[d]||u[d]||a;return n?o.createElement(h,i(i({ref:t},s),{},{components:n})):o.createElement(h,i({ref:t},s))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,i=new Array(a);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var p=2;p<a;p++)i[p]=n[p];return o.createElement.apply(null,i)}return o.createElement.apply(null,n)}m.displayName="MDXCreateElement"},4776:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>p});var o=n(7896),r=(n(2784),n(876));const a={sidebar_position:3},i="Example Hook",l={unversionedId:"client-javascript/example-hook",id:"client-javascript/example-hook",title:"Example Hook",description:"Let's create a hook that will format a text input into a phone number as a user types.",source:"@site/docs/10-client-javascript/example-hook.md",sourceDirName:"10-client-javascript",slug:"/client-javascript/example-hook",permalink:"/docs/client-javascript/example-hook",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:'"Hooks" (not the React kind)',permalink:"/docs/client-javascript/client-hooks"},next:{title:"JS Commands",permalink:"/docs/category/js-commands"}},c={},p=[{value:"Hook Definition",id:"hook-definition",level:2},{value:"Hook Usage",id:"hook-usage",level:2},{value:"Credit \ud83d\ude4c",id:"credit-",level:2}],s={toc:p};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,o.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"example-hook"},"Example Hook"),(0,r.kt)("p",null,"Let's create a hook that will format a text input into a phone number as a user types."),(0,r.kt)("h2",{id:"hook-definition"},"Hook Definition"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Define the hook\nconst PhoneNumber: ViewHook = {\n mounted() {\n this.el.addEventListener("input", (e) => {\n let match = this.el.value.replace(/\\D/g, "").match(/^(\\d{3})(\\d{3})(\\d{4})$/);\n if (match) {\n this.el.value = `${match[1]}-${match[2]}-${match[3]}`;\n }\n });\n },\n};\n// Add the hook to the LiveSocket\nlet liveSocket = new LiveSocket("/live", Socket, {\n hooks: { PhoneNumber },\n});\n')),(0,r.kt)("h2",{id:"hook-usage"},"Hook Usage"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},'<input phx-hook="PhoneNumber" type="text" />\n')),(0,r.kt)("h2",{id:"credit-"},"Credit \ud83d\ude4c"),(0,r.kt)("p",null,"Credit for this example goes to the\n",(0,r.kt)("a",{parentName:"p",href:"https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks-via-phx-hook"},"Phoenix LiveView docs"),". I didn't want\nto reinvent the wheel, so I just copied the example from the Phoenix LiveView docs, added some types, and simplified it\na bit."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/7834bba6.8d670bf6.js b/docs/assets/js/7834bba6.8d670bf6.js deleted file mode 100644 index 224fe80e..00000000 --- a/docs/assets/js/7834bba6.8d670bf6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5967],{876:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>d});var r=t(2784);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function i(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?a(Object(t),!0).forEach((function(n){o(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):a(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function p(e,n){if(null==e)return{};var t,r,o=function(e,n){if(null==e)return{};var t,r,o={},a=Object.keys(e);for(r=0;r<a.length;r++)t=a[r],n.indexOf(t)>=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)t=a[r],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=r.createContext({}),s=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},c=function(e){var n=s(e.components);return r.createElement(l.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},m=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,c=p(e,["components","mdxType","originalType","parentName"]),m=s(t),d=o,h=m["".concat(l,".").concat(d)]||m[d]||u[d]||a;return t?r.createElement(h,i(i({ref:n},c),{},{components:t})):r.createElement(h,i({ref:n},c))}));function d(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=m;var p={};for(var l in n)hasOwnProperty.call(n,l)&&(p[l]=n[l]);p.originalType=e,p.mdxType="string"==typeof e?e:o,i[1]=p;for(var s=2;s<a;s++)i[s]=t[s];return r.createElement.apply(null,i)}return r.createElement.apply(null,t)}m.displayName="MDXCreateElement"},5074:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>p,toc:()=>s});var r=t(7896),o=(t(2784),t(876));const a={sidebar_position:7},i="Push Command",p={unversionedId:"js-commands/push-cmd",id:"js-commands/push-cmd",title:"Push Command",description:"The push command sends an event to the server",source:"@site/docs/11-js-commands/push-cmd.md",sourceDirName:"11-js-commands",slug:"/js-commands/push-cmd",permalink:"/docs/js-commands/push-cmd",draft:!1,tags:[],version:"current",sidebarPosition:7,frontMatter:{sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"Transition Command",permalink:"/docs/js-commands/transition-cmd"},next:{title:"Webserver Integrations",permalink:"/docs/category/webserver-integrations"}},l={},s=[],c={toc:s};function u(e){let{components:n,...t}=e;return(0,o.kt)("wrapper",(0,r.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"push-command"},"Push Command"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"push")," command sends an event to the server"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().push(event: string, options?: PushOptions)\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"event")," - The name of the event to send to the server"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"target")," - An optional selector or component ID to push to"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"loading")," - An optional selector to apply the phx loading classes to"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"page_loading"),' - An optional boolean indicating whether to trigger the "phx:page-loading-start" and\n"phx:page-loading-stop" events. Defaults to ',(0,o.kt)("inlineCode",{parentName:"li"},"false")),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"value")," An optional map of key/value pairs to include in the event's ",(0,o.kt)("inlineCode",{parentName:"li"},"value")," property")))),(0,o.kt)("p",null,"Examples"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// push increment event to server\n<button phx-click="${new JS().push("increment")}">+</button>\n\n// push decrement event to server\n<button phx-click="${new JS().push("decrement")}">-</button>\n\n// push increment event to server with a payload then hide the button\n<button phx-click="${new JS().push("increment", value: {foo: "bar"}).hide()}">\n Increment then hide\n</button>\n\n// hide the button then push increment event\n<button phx-click="${new JS().hide().push("increment")}">Hide then Increment</button>\n\n// push incremenet and show page loading indicator\n<button phx-click="${new JS().push("increment", { page_loading: true })}">\n Page Loading Push\n</button>\n')))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/78c55e7e.59d69983.js b/docs/assets/js/78c55e7e.59d69983.js deleted file mode 100644 index bb1feed2..00000000 --- a/docs/assets/js/78c55e7e.59d69983.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6561],{8416:e=>{e.exports=JSON.parse('{"title":"User Events","description":"How LiveViews listen for user events and the HTML attributes that trigger them ","slug":"/category/user-events","permalink":"/docs/category/user-events","navigation":{"previous":{"title":"Lifecycle of a LiveView","permalink":"/docs/lifecycle-of-a-liveview/intro"},"next":{"title":"User Events / Bindings","permalink":"/docs/user-events-slash-bindings/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/7d980dc2.4c6a6448.js b/docs/assets/js/7d980dc2.4c6a6448.js deleted file mode 100644 index dc6de46e..00000000 --- a/docs/assets/js/7d980dc2.4c6a6448.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9376],{876:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>c});var a=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var d=a.createContext({}),p=function(e){var t=a.useContext(d),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=p(e.components);return a.createElement(d.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,d=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),u=p(n),c=i,k=u["".concat(d,".").concat(c)]||u[c]||m[c]||r;return n?a.createElement(k,o(o({ref:t},s),{},{components:n})):a.createElement(k,o({ref:t},s))}));function c(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=u;var l={};for(var d in t)hasOwnProperty.call(t,d)&&(l[d]=t[d]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}u.displayName="MDXCreateElement"},8062:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(7896),i=(n(2784),n(876));const r={sidebar_position:1},o="LiveViewSocket API",l={unversionedId:"liveview-socket/liveviewsocket-api",id:"liveview-socket/liveviewsocket-api",title:"LiveViewSocket API",description:"The LiveViewSocket API is the second most important API (behind the LiveView API itself). As you've seen, the",source:"@site/docs/04-liveview-socket/liveviewsocket-api.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/liveviewsocket-api",permalink:"/docs/liveview-socket/liveviewsocket-api",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket",permalink:"/docs/category/liveviewsocket"},next:{title:"LiveViewSocket API - Context",permalink:"/docs/liveview-socket/liveviewsocket-api-context"}},d={},p=[{value:"LiveViewSocket Properties and Methods",id:"liveviewsocket-properties-and-methods",level:2},{value:"Related Parts of the API",id:"related-parts-of-the-api",level:2}],s={toc:p};function m(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveviewsocket-api"},"LiveViewSocket API"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," API is the second most important API (behind the LiveView API itself). As you've seen, the\n",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," is passed into (as the ",(0,i.kt)("inlineCode",{parentName:"p"},"socket")," param) just about all of the LiveView API methods (excluding ",(0,i.kt)("inlineCode",{parentName:"p"},"render"),").\nSo far we've mainly used it to update the context of the LiveView. But there is a lot more to ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," and in\nthis section we'll review the full API."),(0,i.kt)("h2",{id:"liveviewsocket-properties-and-methods"},"LiveViewSocket Properties and Methods"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Name"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"id")," (property, read-only)"),(0,i.kt)("td",{parentName:"tr",align:null},"The (random) id of the LiveView")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"connected")," (property, read-only)"),(0,i.kt)("td",{parentName:"tr",align:null},"Whether the websocket is connected. ",(0,i.kt)("inlineCode",{parentName:"td"},"true")," if connected to a websocket, ",(0,i.kt)("inlineCode",{parentName:"td"},"false")," for http request")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"context")," (property, read-only)"),(0,i.kt)("td",{parentName:"tr",align:null},"The current context (i.e., state) of the LiveView")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"assign(context:Partial<TContext>):void;")),(0,i.kt)("td",{parentName:"tr",align:null},"Update the context (i.e., state) of the LiveView")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"tempAssign(context:Partial<TContext>):void;")),(0,i.kt)("td",{parentName:"tr",align:null},"Marks any set properties as temporary and will be reset to the given value after the next render cycle. Typically used to ensure large but infrequently updated values are not kept in memory.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pageTitle(newPageTitle:string):void;")),(0,i.kt)("td",{parentName:"tr",align:null},"Updates the ",(0,i.kt)("inlineCode",{parentName:"td"},"<title>")," tag of the LiveView. Requires using the ",(0,i.kt)("inlineCode",{parentName:"td"},"live_title")," helper in rendering the page.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"putFlash(key: string, value: string): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Add flash message to the socket for a given key and value.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pushEvent(pushEvent: AnyLivePushEvent): void;")),(0,i.kt)("td",{parentName:"tr",align:null},"Pushes an event from the server to the client. Requires a client ",(0,i.kt)("inlineCode",{parentName:"td"},"Hook")," to be defined and to be listening for the event via ",(0,i.kt)("inlineCode",{parentName:"td"},"this.handleEvent")," callback.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pushPatch(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Updates the LiveView's browser URL with the given path and query parameters.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pushRedirect(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Shutdowns the current LiveView and loads another LiveView in its place without reloading the whole page (i.e., making a full HTTP request). Can be used to remount the current LiveView if need be. Use ",(0,i.kt)("inlineCode",{parentName:"td"},"pushPatch")," to update the current LiveView without unloading and remounting.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"sendInfo(info: Info<TInfos>): void;")),(0,i.kt)("td",{parentName:"tr",align:null},'Send an internal event (a.k.a "Info") to the LiveView\'s ',(0,i.kt)("inlineCode",{parentName:"td"},"handleInfo")," method")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"subscribe(topic: string): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Subscribe to the given topic using pub/sub. Events published to this topic will be delivered to ",(0,i.kt)("inlineCode",{parentName:"td"},"handleInfo"),".")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"repeat(fn: () => void, intervalMillis: number): void;")),(0,i.kt)("td",{parentName:"tr",align:null},"Runs the given function on the given interval until this LiveView is unloaded.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"allowUpload(name: string, options?: UploadConfigOptions): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Allows file uploads for the given LiveView and configures the upload options (filetypes, size, etc).")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"cancelUpload(configName: string, ref: string): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Cancels the file upload for a given UploadConfig by config name and file ref.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"consumeUploadedEntries<T>(configName: string,fn: (meta: ConsumeUploadedEntriesMeta, entry: UploadEntry) => Promise<T>):Promise<T[]>;")),(0,i.kt)("td",{parentName:"tr",align:null},'Consume the uploaded files for a given UploadConfig (by name). This should only be called after the form\'s "save" event has occurred which guarantees all the files for the upload have been fully uploaded.')),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"uploadedEntries(configName: string): Promise<{completed: UploadEntry[];inProgress: UploadEntry[];}>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Returns two sets of files that are being uploaded, those ",(0,i.kt)("inlineCode",{parentName:"td"},"completed")," and those ",(0,i.kt)("inlineCode",{parentName:"td"},"inProgress")," for a given UploadConfig (by name). Unlike ",(0,i.kt)("inlineCode",{parentName:"td"},"consumeUploadedEntries"),', this does not require the form\'s "save" event to have occurred and will not throw if any of the entries are not fully uploaded.')))),(0,i.kt)("h2",{id:"related-parts-of-the-api"},"Related Parts of the API"),(0,i.kt)("p",null,"We'll look at related parts of the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," API over the next few sections:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Reading and Writing the Context"),(0,i.kt)("li",{parentName:"ul"},"Pushing Updates to the Client"),(0,i.kt)("li",{parentName:"ul"},"Server Events / Pub/Sub"),(0,i.kt)("li",{parentName:"ul"},"File Uploads"),(0,i.kt)("li",{parentName:"ul"},"Everything Else")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/814f3328.63c77d93.js b/docs/assets/js/814f3328.63c77d93.js deleted file mode 100644 index a81191ae..00000000 --- a/docs/assets/js/814f3328.63c77d93.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2535],{5641:e=>{e.exports=JSON.parse('{"title":"Recent posts","items":[{"title":"Welcome","permalink":"/blog/welcome"},{"title":"MDX Blog Post","permalink":"/blog/mdx-blog-post"},{"title":"Long Blog Post","permalink":"/blog/long-blog-post"},{"title":"First Blog Post","permalink":"/blog/first-blog-post"}]}')}}]); \ No newline at end of file diff --git a/docs/assets/js/8717b14a.57b843ef.js b/docs/assets/js/8717b14a.57b843ef.js deleted file mode 100644 index 3c247c32..00000000 --- a/docs/assets/js/8717b14a.57b843ef.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[948],{876:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>f});var r=o(2784);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function l(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function a(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?l(Object(o),!0).forEach((function(t){n(e,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):l(Object(o)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))}))}return e}function i(e,t){if(null==e)return{};var o,r,n=function(e,t){if(null==e)return{};var o,r,n={},l=Object.keys(e);for(r=0;r<l.length;r++)o=l[r],t.indexOf(o)>=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r<l.length;r++)o=l[r],t.indexOf(o)>=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var s=r.createContext({}),p=function(e){var t=r.useContext(s),o=t;return e&&(o="function"==typeof e?e(t):a(a({},t),e)),o},c=function(e){var t=p(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},g=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,l=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),g=p(o),f=n,m=g["".concat(s,".").concat(f)]||g[f]||u[f]||l;return o?r.createElement(m,a(a({ref:t},c),{},{components:o})):r.createElement(m,a({ref:t},c))}));function f(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var l=o.length,a=new Array(l);a[0]=g;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i.mdxType="string"==typeof e?e:n,a[1]=i;for(var p=2;p<l;p++)a[p]=o[p];return r.createElement.apply(null,a)}return r.createElement.apply(null,o)}g.displayName="MDXCreateElement"},457:(e,t,o)=>{o.r(t),o.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>u,frontMatter:()=>l,metadata:()=>i,toc:()=>p});var r=o(7896),n=(o(2784),o(876));const l={slug:"long-blog-post",title:"Long Blog Post",authors:"floodfx",tags:["hello","docusaurus"]},a=void 0,i={permalink:"/blog/long-blog-post",source:"@site/blog/2019-05-29-long-blog-post.md",title:"Long Blog Post",description:"This is the summary of a very long blog post,",date:"2019-05-29T00:00:00.000Z",formattedDate:"May 29, 2019",tags:[{label:"hello",permalink:"/blog/tags/hello"},{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:2.05,hasTruncateMarker:!0,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"long-blog-post",title:"Long Blog Post",authors:"floodfx",tags:["hello","docusaurus"]},prevItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"},nextItem:{title:"First Blog Post",permalink:"/blog/first-blog-post"}},s={authorsImageUrls:[void 0]},p=[],c={toc:p};function u(e){let{components:t,...o}=e;return(0,n.kt)("wrapper",(0,r.Z)({},c,o,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"This is the summary of a very long blog post,"),(0,n.kt)("p",null,"Use a ",(0,n.kt)("inlineCode",{parentName:"p"},"\x3c!--")," ",(0,n.kt)("inlineCode",{parentName:"p"},"truncate")," ",(0,n.kt)("inlineCode",{parentName:"p"},"--\x3e")," comment to limit blog post size in the list view."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/88861ccc.20e78eba.js b/docs/assets/js/88861ccc.20e78eba.js deleted file mode 100644 index abef400f..00000000 --- a/docs/assets/js/88861ccc.20e78eba.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[645],{876:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(2784);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){o(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},p=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=l(r),f=o,v=d["".concat(c,".").concat(f)]||d[f]||u[f]||i;return r?n.createElement(v,a(a({ref:t},p),{},{components:r})):n.createElement(v,a({ref:t},p))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=d;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var l=2;l<i;l++)a[l]=r[l];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}d.displayName="MDXCreateElement"},1677:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var n=r(7896),o=(r(2784),r(876));const i={sidebar_position:4},a="Getting Involved",s={unversionedId:"overview/feedback",id:"overview/feedback",title:"Getting Involved",description:"Feedback is a \ud83c\udf81",source:"@site/docs/01-overview/feedback.md",sourceDirName:"01-overview",slug:"/overview/feedback",permalink:"/docs/overview/feedback",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Packages & Runtimes",permalink:"/docs/overview/runtimes"},next:{title:"Gratitude",permalink:"/docs/overview/gratitude"}},c={},l=[{value:"Feedback is a \ud83c\udf81",id:"feedback-is-a-",level:2},{value:"Contributing is \u2764\ufe0f",id:"contributing-is-\ufe0f",level:2},{value:"Sponsorship is \u26fd\ufe0f",id:"sponsorship-is-\ufe0f",level:2}],p={toc:l};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"getting-involved"},"Getting Involved"),(0,o.kt)("h2",{id:"feedback-is-a-"},"Feedback is a \ud83c\udf81"),(0,o.kt)("p",null,"Like all software, this is a work in progress. If you have any feedback, please let us know by opening an issue on the\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/issues"},"GitHub repository"),"."),(0,o.kt)("h2",{id:"contributing-is-\ufe0f"},"Contributing is \u2764\ufe0f"),(0,o.kt)("p",null,"We welcome questions, comments, documentation, and code contributions. If you'd like to contribute, please feel free to\nopen an issue or a pull request. We'll do our best to respond quickly and get it merged!"),(0,o.kt)("h2",{id:"sponsorship-is-\ufe0f"},"Sponsorship is \u26fd\ufe0f"),(0,o.kt)("p",null,"If you'd like to support the project, please consider sponsoring us on ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/sponsors/floodfx"},"GitHub"),".\nWe'll use the funds to pay for coffee, hosting, domain names, therapy, and other expenses related to the project."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/88b645ee.bbd3a54f.js b/docs/assets/js/88b645ee.bbd3a54f.js deleted file mode 100644 index dcd44997..00000000 --- a/docs/assets/js/88b645ee.bbd3a54f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8168],{1536:e=>{e.exports=JSON.parse('{"title":"Overview","description":"LiveViewJS is an open-source framework for \\"LiveView\\"-based, full-stack applications in NodeJS and Deno.","slug":"/category/overview","permalink":"/docs/category/overview","navigation":{"next":{"title":"Introduction","permalink":"/docs/overview/introduction"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/8908bd66.557693cf.js b/docs/assets/js/8908bd66.557693cf.js deleted file mode 100644 index ef19f1f9..00000000 --- a/docs/assets/js/8908bd66.557693cf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6527],{876:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>m});var i=t(2784);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function s(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?a(Object(t),!0).forEach((function(n){r(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):a(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function o(e,n){if(null==e)return{};var t,i,r=function(e,n){if(null==e)return{};var t,i,r={},a=Object.keys(e);for(i=0;i<a.length;i++)t=a[i],n.indexOf(t)>=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i<a.length;i++)t=a[i],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var l=i.createContext({}),u=function(e){var n=i.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):s(s({},n),e)),t},c=function(e){var n=u(e.components);return i.createElement(l.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},d=i.forwardRef((function(e,n){var t=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),d=u(t),m=r,b=d["".concat(l,".").concat(m)]||d[m]||p[m]||a;return t?i.createElement(b,s(s({ref:n},c),{},{components:t})):i.createElement(b,s({ref:n},c))}));function m(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var a=t.length,s=new Array(a);s[0]=d;var o={};for(var l in n)hasOwnProperty.call(n,l)&&(o[l]=n[l]);o.originalType=e,o.mdxType="string"==typeof e?e:r,s[1]=o;for(var u=2;u<a;u++)s[u]=t[u];return i.createElement.apply(null,s)}return i.createElement.apply(null,t)}d.displayName="MDXCreateElement"},5289:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>a,metadata:()=>o,toc:()=>u});var i=t(7896),r=(t(2784),t(876));const a={sidebar_position:3},s="Rate Limiting Bindings",o={unversionedId:"user-events-slash-bindings/rate-limiting-bindings",id:"user-events-slash-bindings/rate-limiting-bindings",title:"Rate Limiting Bindings",description:"Deboucing and throttling user events is a very common need and to support these use-cases there are the following",source:"@site/docs/06-user-events-slash-bindings/rate-limiting-bindings.md",sourceDirName:"06-user-events-slash-bindings",slug:"/user-events-slash-bindings/rate-limiting-bindings",permalink:"/docs/user-events-slash-bindings/rate-limiting-bindings",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Additional Bindings",permalink:"/docs/user-events-slash-bindings/additional-bindings"},next:{title:"Phoenix LiveView Bindings",permalink:"/docs/user-events-slash-bindings/bindings-table"}},l={},u=[{value:"Debounce Examples",id:"debounce-examples",level:2},{value:"Debounce input blur",id:"debounce-input-blur",level:3},{value:"Debouce input change",id:"debouce-input-change",level:3},{value:"Throttle Example",id:"throttle-example",level:2}],c={toc:u};function p(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,i.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"rate-limiting-bindings"},"Rate Limiting Bindings"),(0,r.kt)("p",null,"Deboucing and throttling user events is a very common need and to support these use-cases there are the following\nbindings:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"phx-debounce")," - Debounce an event by the number of milliseconds specified in the attribute value ",(0,r.kt)("strong",{parentName:"li"},"or")," by setting\nthe value to ",(0,r.kt)("inlineCode",{parentName:"li"},"blur"),". This is useful for preventing multiple events from being sent to the server in rapid succession.\nWhen ",(0,r.kt)("inlineCode",{parentName:"li"},"blur")," is the value, the event will be debounced until the element is blurred by the user. Typically used for\ninput elements."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"phx-throttle")," - Throttle an event by the number of milliseconds specified in the attribute value. In contrast to\ndebouncing, throttling emits an event immediately and then only once every specified number of milliseconds. Typically\nused to rate limit click events, key events, and mouse actions.")),(0,r.kt)("h2",{id:"debounce-examples"},"Debounce Examples"),(0,r.kt)("p",null,"Here are some examples of the debounce binding in action."),(0,r.kt)("h3",{id:"debounce-input-blur"},"Debounce input blur"),(0,r.kt)("p",null,"Let's say we want to send the ",(0,r.kt)("inlineCode",{parentName:"p"},"validate")," event to the server when a user blurs away from the address input in a form:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},'<form action="#" phx-change="validate" phx-submit="save">\n \x3c!--// only send "validate" event when address input is blurred --\x3e\n <input name="address" phx-debounce="blur" />\n</form>\n')),(0,r.kt)("h3",{id:"debouce-input-change"},"Debouce input change"),(0,r.kt)("p",null,"Let's say we only want to send the ",(0,r.kt)("inlineCode",{parentName:"p"},"search")," event to the server 1 second after a user stops typing into the search\ninput:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},'<form action="#" phx-change="search">\n \x3c!--// send "search" event after 1 second of debouncing --\x3e\n <input name="query" phx-debounce="1000" />\n</form>\n')),(0,r.kt)("h2",{id:"throttle-example"},"Throttle Example"),(0,r.kt)("p",null,"Let's say we only want to send one ",(0,r.kt)("inlineCode",{parentName:"p"},"volume_up")," event every 500ms. The user can click the button as many times as they\nwant, but the event will only be sent to the server every 500ms:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},'\x3c!--// rate limit clicks on a volume up event --\x3e\n<button phx-click="volume_up" phx-throttle="500" />\n')))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/925b3f96.2a134060.js b/docs/assets/js/925b3f96.2a134060.js deleted file mode 100644 index 605d8123..00000000 --- a/docs/assets/js/925b3f96.2a134060.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9003],{876:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var o=r(2784);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function l(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){n(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function a(e,t){if(null==e)return{};var r,o,n=function(e,t){if(null==e)return{};var r,o,n={},i=Object.keys(e);for(o=0;o<i.length;o++)r=i[o],t.indexOf(r)>=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o<i.length;o++)r=i[o],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=o.createContext({}),u=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},c=function(e){var t=u(e.components);return o.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,c=a(e,["components","mdxType","originalType","parentName"]),m=u(r),f=n,g=m["".concat(s,".").concat(f)]||m[f]||p[f]||i;return r?o.createElement(g,l(l({ref:t},c),{},{components:r})):o.createElement(g,l({ref:t},c))}));function f(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=r.length,l=new Array(i);l[0]=m;var a={};for(var s in t)hasOwnProperty.call(t,s)&&(a[s]=t[s]);a.originalType=e,a.mdxType="string"==typeof e?e:n,l[1]=a;for(var u=2;u<i;u++)l[u]=r[u];return o.createElement.apply(null,l)}return o.createElement.apply(null,r)}m.displayName="MDXCreateElement"},6954:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>p,frontMatter:()=>i,metadata:()=>a,toc:()=>u});var o=r(7896),n=(r(2784),r(876));const i={slug:"first-blog-post",title:"First Blog Post",authors:["floodfx"],tags:["hola","docusaurus"]},l=void 0,a={permalink:"/blog/first-blog-post",source:"@site/blog/2019-05-28-first-blog-post.md",title:"First Blog Post",description:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum",date:"2019-05-28T00:00:00.000Z",formattedDate:"May 28, 2019",tags:[{label:"hola",permalink:"/blog/tags/hola"},{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:.12,hasTruncateMarker:!1,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"first-blog-post",title:"First Blog Post",authors:["floodfx"],tags:["hola","docusaurus"]},prevItem:{title:"Long Blog Post",permalink:"/blog/long-blog-post"}},s={authorsImageUrls:[void 0]},u=[],c={toc:u};function p(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/935f2afb.42240f25.js b/docs/assets/js/935f2afb.42240f25.js deleted file mode 100644 index 43a77976..00000000 --- a/docs/assets/js/935f2afb.42240f25.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"category","label":"Overview","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Introduction","href":"/docs/overview/introduction","docId":"overview/introduction"},{"type":"link","label":"LiveView Paradigm","href":"/docs/overview/paradigm","docId":"overview/paradigm"},{"type":"link","label":"Packages & Runtimes","href":"/docs/overview/runtimes","docId":"overview/runtimes"},{"type":"link","label":"Getting Involved","href":"/docs/overview/feedback","docId":"overview/feedback"},{"type":"link","label":"Gratitude","href":"/docs/overview/gratitude","docId":"overview/gratitude"}],"href":"/docs/category/overview"},{"type":"category","label":"Quick Starts","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Download the Repo","href":"/docs/quick-starts/get-liveviewjs-repo","docId":"quick-starts/get-liveviewjs-repo"},{"type":"link","label":"NodeJS - Run the Examples","href":"/docs/quick-starts/nodejs-run-examples","docId":"quick-starts/nodejs-run-examples"},{"type":"link","label":"NodeJS - Build a LiveView","href":"/docs/quick-starts/nodejs-build-first-liveview","docId":"quick-starts/nodejs-build-first-liveview"},{"type":"link","label":"Deno - Run the Examples","href":"/docs/quick-starts/deno-run-examples","docId":"quick-starts/deno-run-examples"},{"type":"link","label":"Deno - Build a LiveView","href":"/docs/quick-starts/deno-build-first-liveview","docId":"quick-starts/deno-build-first-liveview"}],"href":"/docs/category/quick-starts"},{"type":"category","label":"Anatomy of a LiveView","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"LiveView API","href":"/docs/anatomy-of-a-liveview/liveview-api","docId":"anatomy-of-a-liveview/liveview-api"},{"type":"link","label":"LiveView API - `mount`","href":"/docs/anatomy-of-a-liveview/mount-details","docId":"anatomy-of-a-liveview/mount-details"},{"type":"link","label":"LiveView API - `render`","href":"/docs/anatomy-of-a-liveview/render-details","docId":"anatomy-of-a-liveview/render-details"},{"type":"link","label":"LiveView API - `handleEvent`","href":"/docs/anatomy-of-a-liveview/handle-event-details","docId":"anatomy-of-a-liveview/handle-event-details"},{"type":"link","label":"LiveView API - `handleParams`","href":"/docs/anatomy-of-a-liveview/handle-params","docId":"anatomy-of-a-liveview/handle-params"},{"type":"link","label":"LiveView API - `handleInfo`","href":"/docs/anatomy-of-a-liveview/handle-info","docId":"anatomy-of-a-liveview/handle-info"},{"type":"link","label":"User-Initiated Event with `handleInfo`","href":"/docs/anatomy-of-a-liveview/handle-info-user-initiated","docId":"anatomy-of-a-liveview/handle-info-user-initiated"},{"type":"link","label":"Background Task with `handleInfo`","href":"/docs/anatomy-of-a-liveview/handle-info-background-task","docId":"anatomy-of-a-liveview/handle-info-background-task"},{"type":"link","label":"Pub/Sub with `handleInfo`","href":"/docs/anatomy-of-a-liveview/handle-info-pub-sub","docId":"anatomy-of-a-liveview/handle-info-pub-sub"}],"href":"/docs/category/anatomy-of-a-liveview"},{"type":"category","label":"LiveViewSocket","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"LiveViewSocket API","href":"/docs/liveview-socket/liveviewsocket-api","docId":"liveview-socket/liveviewsocket-api"},{"type":"link","label":"LiveViewSocket API - Context","href":"/docs/liveview-socket/liveviewsocket-api-context","docId":"liveview-socket/liveviewsocket-api-context"},{"type":"link","label":"LiveViewSocket API - Push","href":"/docs/liveview-socket/liveviewsocket-api-push","docId":"liveview-socket/liveviewsocket-api-push"},{"type":"link","label":"LiveViewSocket API - Server Events","href":"/docs/liveview-socket/liveviewsocket-api-infos","docId":"liveview-socket/liveviewsocket-api-infos"},{"type":"link","label":"LiveViewSocket API - Uploads","href":"/docs/liveview-socket/liveviewsocket-api-uploads","docId":"liveview-socket/liveviewsocket-api-uploads"},{"type":"link","label":"LiveViewSocket API - Misc","href":"/docs/liveview-socket/liveviewsocket-api-misc","docId":"liveview-socket/liveviewsocket-api-misc"},{"type":"link","label":"Raw `LiveViewSocket` API","href":"/docs/liveview-socket/raw-liveviewsocket-api","docId":"liveview-socket/raw-liveviewsocket-api"}],"href":"/docs/category/liveviewsocket"},{"type":"category","label":"Lifecycle of a LiveView","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Lifecycle of a LiveView","href":"/docs/lifecycle-of-a-liveview/intro","docId":"lifecycle-of-a-liveview/intro"}],"href":"/docs/category/lifecycle-of-a-liveview"},{"type":"category","label":"User Events","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"User Events / Bindings","href":"/docs/user-events-slash-bindings/overview","docId":"user-events-slash-bindings/overview"},{"type":"link","label":"Additional Bindings","href":"/docs/user-events-slash-bindings/additional-bindings","docId":"user-events-slash-bindings/additional-bindings"},{"type":"link","label":"Rate Limiting Bindings","href":"/docs/user-events-slash-bindings/rate-limiting-bindings","docId":"user-events-slash-bindings/rate-limiting-bindings"},{"type":"link","label":"Phoenix LiveView Bindings","href":"/docs/user-events-slash-bindings/bindings-table","docId":"user-events-slash-bindings/bindings-table"}],"href":"/docs/category/user-events"},{"type":"category","label":"Forms & Changesets","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/docs/forms-and-changesets/overview","docId":"forms-and-changesets/overview"},{"type":"link","label":"Changesets","href":"/docs/forms-and-changesets/changesets","docId":"forms-and-changesets/changesets"},{"type":"link","label":"Forms & Changesets Example","href":"/docs/forms-and-changesets/use-with-forms","docId":"forms-and-changesets/use-with-forms"}],"href":"/docs/category/forms--changesets"},{"type":"category","label":"Uploading Files","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/docs/file-upload/overview","docId":"file-upload/overview"},{"type":"link","label":"Built-in Image Preview","href":"/docs/file-upload/image-preview","docId":"file-upload/image-preview"},{"type":"link","label":"Built-in Drag and Drop","href":"/docs/file-upload/drag-and-drop","docId":"file-upload/drag-and-drop"},{"type":"link","label":"Upload Config Options","href":"/docs/file-upload/upload-config-options","docId":"file-upload/upload-config-options"}],"href":"/docs/category/uploading-files"},{"type":"category","label":"Real-time / Multi-player","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/docs/real-time-multi-player-pub-sub/overview","docId":"real-time-multi-player-pub-sub/overview"},{"type":"link","label":"Example Pub/Sub LiveView","href":"/docs/real-time-multi-player-pub-sub/example-pub-sub","docId":"real-time-multi-player-pub-sub/example-pub-sub"}],"href":"/docs/category/real-time--multi-player"},{"type":"category","label":"Client-side Javascript","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Client-side Javascript","href":"/docs/client-javascript/overview","docId":"client-javascript/overview"},{"type":"link","label":"\\"Hooks\\" (not the React kind)","href":"/docs/client-javascript/client-hooks","docId":"client-javascript/client-hooks"},{"type":"link","label":"Example Hook","href":"/docs/client-javascript/example-hook","docId":"client-javascript/example-hook"}],"href":"/docs/category/client-side-javascript"},{"type":"category","label":"JS Commands","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/docs/js-commands/overview","docId":"js-commands/overview"},{"type":"link","label":"Add/Remove Class Commands","href":"/docs/js-commands/add-remove-class","docId":"js-commands/add-remove-class"},{"type":"link","label":"Set/Remove Attribute Commands","href":"/docs/js-commands/set-remove-attr","docId":"js-commands/set-remove-attr"},{"type":"link","label":"Show/Hide/Toggle Element Commands","href":"/docs/js-commands/show-hide-toggle-el","docId":"js-commands/show-hide-toggle-el"},{"type":"link","label":"Dispatch Command","href":"/docs/js-commands/dispatch-cmd","docId":"js-commands/dispatch-cmd"},{"type":"link","label":"Transition Command","href":"/docs/js-commands/transition-cmd","docId":"js-commands/transition-cmd"},{"type":"link","label":"Push Command","href":"/docs/js-commands/push-cmd","docId":"js-commands/push-cmd"}],"href":"/docs/category/js-commands"},{"type":"category","label":"Webserver Integrations","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Webserver Integration","href":"/docs/webserver-integration/overview","docId":"webserver-integration/overview"},{"type":"link","label":"LiveViewServerAdaptor","href":"/docs/webserver-integration/liveview-server-adaptor","docId":"webserver-integration/liveview-server-adaptor"},{"type":"link","label":"Support Webserver \\"X\\"","href":"/docs/webserver-integration/support-webserver-x","docId":"webserver-integration/support-webserver-x"}],"href":"/docs/category/webserver-integrations"},{"type":"category","label":"Miscellaneous","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Security Topics","href":"/docs/misc/security-topics","docId":"misc/security-topics"},{"type":"link","label":"Live Components","href":"/docs/misc/livecomponents","docId":"misc/livecomponents"},{"type":"link","label":"Statics and Dynamics","href":"/docs/misc/statics-and-dynamics","docId":"misc/statics-and-dynamics"},{"type":"link","label":"Debugging LiveViews","href":"/docs/misc/debugging-wire","docId":"misc/debugging-wire"},{"type":"link","label":"Root and Page Renderers","href":"/docs/misc/root-and-page-renderers","docId":"misc/root-and-page-renderers"},{"type":"link","label":"Flash Messages","href":"/docs/misc/flash","docId":"misc/flash"}],"href":"/docs/category/miscellaneous"}]},"docs":{"anatomy-of-a-liveview/handle-event-details":{"id":"anatomy-of-a-liveview/handle-event-details","title":"LiveView API - `handleEvent`","description":"handleEvent is called automatically by the LiveViewJS framework when a user action causes the browser to send","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/handle-info":{"id":"anatomy-of-a-liveview/handle-info","title":"LiveView API - `handleInfo`","description":"handleInfo is how server-side events (a.k.a Info) are handled. These server-side events are initiated by processes","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/handle-info-background-task":{"id":"anatomy-of-a-liveview/handle-info-background-task","title":"Background Task with `handleInfo`","description":"A \\"live\\" dashboard that updates with the latest metrics periodically is another use case that shines with server events","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/handle-info-pub-sub":{"id":"anatomy-of-a-liveview/handle-info-pub-sub","title":"Pub/Sub with `handleInfo`","description":"Pub/Sub is a common pattern for decoupling processes by allowing messages to be sent to a topic by one process and","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/handle-info-user-initiated":{"id":"anatomy-of-a-liveview/handle-info-user-initiated","title":"User-Initiated Event with `handleInfo`","description":"Search is a common use case where a user-initiated event might be handled by handleInfo.","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/handle-params":{"id":"anatomy-of-a-liveview/handle-params","title":"LiveView API - `handleParams`","description":"Let\'s explore the handleParams method. Since the previous example (counterLiveView) did not use handleParams,","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/liveview-api":{"id":"anatomy-of-a-liveview/liveview-api","title":"LiveView API","description":"We are going to be using Typescript in our examples because LiveViewJS is very thoroughly typed, which","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/mount-details":{"id":"anatomy-of-a-liveview/mount-details","title":"LiveView API - `mount`","description":"mount is called by the LiveViewJS runtime when your LiveView is first mounted","sidebar":"tutorialSidebar"},"anatomy-of-a-liveview/render-details":{"id":"anatomy-of-a-liveview/render-details","title":"LiveView API - `render`","description":"render is responsible for taking the context (i.e., state) of the LiveView and generating the HTML/CSS for the","sidebar":"tutorialSidebar"},"client-javascript/client-hooks":{"id":"client-javascript/client-hooks","title":"\\"Hooks\\" (not the React kind)","description":"Sometimes you need to do something that is not supported by any of the existing user event bindings or that requires","sidebar":"tutorialSidebar"},"client-javascript/example-hook":{"id":"client-javascript/example-hook","title":"Example Hook","description":"Let\'s create a hook that will format a text input into a phone number as a user types.","sidebar":"tutorialSidebar"},"client-javascript/overview":{"id":"client-javascript/overview","title":"Client-side Javascript","description":"LiveViewJS pages do, in fact, require some client-side javascript to be loaded as part of the HTML page. This","sidebar":"tutorialSidebar"},"file-upload/drag-and-drop":{"id":"file-upload/drag-and-drop","title":"Built-in Drag and Drop","description":"LiveViewJS ships with built-in support for drag and drop file uploads. It is incredibly easy to use. All you need to do","sidebar":"tutorialSidebar"},"file-upload/image-preview":{"id":"file-upload/image-preview","title":"Built-in Image Preview","description":"LiveViewJS ships with build-in support for image previews when uploading files.","sidebar":"tutorialSidebar"},"file-upload/overview":{"id":"file-upload/overview","title":"Overview","description":"File uploads are another common feature of web applications. LiveViewJS provides built in support for file uploads,","sidebar":"tutorialSidebar"},"file-upload/upload-config-options":{"id":"file-upload/upload-config-options","title":"Upload Config Options","description":"These are the options you can pass into the allowUpload method to configure the upload.","sidebar":"tutorialSidebar"},"forms-and-changesets/changesets":{"id":"forms-and-changesets/changesets","title":"Changesets","description":"High Level","sidebar":"tutorialSidebar"},"forms-and-changesets/overview":{"id":"forms-and-changesets/overview","title":"Overview","description":"Forms are obviously extremely important for any web application that needs user input. Building, validating, and","sidebar":"tutorialSidebar"},"forms-and-changesets/use-with-forms":{"id":"forms-and-changesets/use-with-forms","title":"Forms & Changesets Example","description":"Now that we\'ve revisited Form Events and learned about Changesets, let\'s take a look at how we can use them together to","sidebar":"tutorialSidebar"},"js-commands/add-remove-class":{"id":"js-commands/add-remove-class","title":"Add/Remove Class Commands","description":"Add or remove css classes including optional transition classes from an element using the addclass and removeclass","sidebar":"tutorialSidebar"},"js-commands/dispatch-cmd":{"id":"js-commands/dispatch-cmd","title":"Dispatch Command","description":"The dispatch command dispatches a DOM event from the target element","sidebar":"tutorialSidebar"},"js-commands/overview":{"id":"js-commands/overview","title":"Overview","description":"LiveViewJS user events (clicks, etc) typically trigger a server-side event which updates the state of the LiveView and","sidebar":"tutorialSidebar"},"js-commands/push-cmd":{"id":"js-commands/push-cmd","title":"Push Command","description":"The push command sends an event to the server","sidebar":"tutorialSidebar"},"js-commands/set-remove-attr":{"id":"js-commands/set-remove-attr","title":"Set/Remove Attribute Commands","description":"Set or remove an attribute from an HTML element using the setattribute and removeattribute commands.","sidebar":"tutorialSidebar"},"js-commands/show-hide-toggle-el":{"id":"js-commands/show-hide-toggle-el","title":"Show/Hide/Toggle Element Commands","description":"The show, hide, and toggle commands are used to show, hide, or toggle the visibility of an element including css","sidebar":"tutorialSidebar"},"js-commands/transition-cmd":{"id":"js-commands/transition-cmd","title":"Transition Command","description":"The transition command dispatches a DOM event from the target element","sidebar":"tutorialSidebar"},"lifecycle-of-a-liveview/intro":{"id":"lifecycle-of-a-liveview/intro","title":"Lifecycle of a LiveView","description":"We are going to look at the lifecycle of LiveViews in detail to see when each LiveView method (e.g., mount,","sidebar":"tutorialSidebar"},"liveview-socket/liveviewsocket-api":{"id":"liveview-socket/liveviewsocket-api","title":"LiveViewSocket API","description":"The LiveViewSocket API is the second most important API (behind the LiveView API itself). As you\'ve seen, the","sidebar":"tutorialSidebar"},"liveview-socket/liveviewsocket-api-context":{"id":"liveview-socket/liveviewsocket-api-context","title":"LiveViewSocket API - Context","description":"The \\"context\\" of a LiveView is the current state of the LiveView. One way to think of a LiveView is as a set of methods","sidebar":"tutorialSidebar"},"liveview-socket/liveviewsocket-api-infos":{"id":"liveview-socket/liveviewsocket-api-infos","title":"LiveViewSocket API - Server Events","description":"Server events are important to connect LiveViews with asynchronous processes. For example, a LiveView may need to wait","sidebar":"tutorialSidebar"},"liveview-socket/liveviewsocket-api-misc":{"id":"liveview-socket/liveviewsocket-api-misc","title":"LiveViewSocket API - Misc","description":"A few other methods and properties are available on the LiveViewSocket object that we haven\'t covered yet.","sidebar":"tutorialSidebar"},"liveview-socket/liveviewsocket-api-push":{"id":"liveview-socket/liveviewsocket-api-push","title":"LiveViewSocket API - Push","description":"There are various methods for \\"pushing\\" data from the server to the client outside of render and updating the URL of","sidebar":"tutorialSidebar"},"liveview-socket/liveviewsocket-api-uploads":{"id":"liveview-socket/liveviewsocket-api-uploads","title":"LiveViewSocket API - Uploads","description":"A common use case for a web application is to allow users to upload files. The following methods on the LiveViewSocket","sidebar":"tutorialSidebar"},"liveview-socket/raw-liveviewsocket-api":{"id":"liveview-socket/raw-liveviewsocket-api","title":"Raw `LiveViewSocket` API","description":"For your reference, below is the raw Typescript for the LiveViewSocket interface (copied from","sidebar":"tutorialSidebar"},"misc/debugging-wire":{"id":"misc/debugging-wire","title":"Debugging LiveViews","description":"","sidebar":"tutorialSidebar"},"misc/flash":{"id":"misc/flash","title":"Flash Messages","description":"","sidebar":"tutorialSidebar"},"misc/livecomponents":{"id":"misc/livecomponents","title":"Live Components","description":"We\'ve mostly been talking about LiveViews, but LiveViewJS also supports LiveComponents. Live Components are a way","sidebar":"tutorialSidebar"},"misc/root-and-page-renderers":{"id":"misc/root-and-page-renderers","title":"Root and Page Renderers","description":"","sidebar":"tutorialSidebar"},"misc/security-topics":{"id":"misc/security-topics","title":"Security Topics","description":"Here are some security topics to consider when using LiveViewJS.","sidebar":"tutorialSidebar"},"misc/statics-and-dynamics":{"id":"misc/statics-and-dynamics","title":"Statics and Dynamics","description":"It is helpful to understand why and how LiveViewJS takes a HTML template and breaks it into static and dynamic","sidebar":"tutorialSidebar"},"overview/feedback":{"id":"overview/feedback","title":"Getting Involved","description":"Feedback is a \ud83c\udf81","sidebar":"tutorialSidebar"},"overview/gratitude":{"id":"overview/gratitude","title":"Gratitude","description":"\ud83d\ude4c Thanks to the Phoenix LiveView team for the genius conceptualization and implementation of LiveViews and all the code","sidebar":"tutorialSidebar"},"overview/introduction":{"id":"overview/introduction","title":"Introduction","description":"LiveViewJS is an open-source framework for \\"LiveView\\"-based, full-stack applications in NodeJS and Deno.","sidebar":"tutorialSidebar"},"overview/paradigm":{"id":"overview/paradigm","title":"LiveView Paradigm","description":"The LiveView model is simple. The server renders an HTML page when a user makes the initial HTTP request. That page","sidebar":"tutorialSidebar"},"overview/runtimes":{"id":"overview/runtimes","title":"Packages & Runtimes","description":"LiveViewJS is written in Typescript and runs on both NodeJS and Deno.","sidebar":"tutorialSidebar"},"quick-starts/deno-build-first-liveview":{"id":"quick-starts/deno-build-first-liveview","title":"Deno - Build a LiveView","description":"Since you\'ve already downloaded the LiveViewJS repo, it should be easy to create a new","sidebar":"tutorialSidebar"},"quick-starts/deno-run-examples":{"id":"quick-starts/deno-run-examples","title":"Deno - Run the Examples","description":"LiveViewJS ships with over a dozen example LiveViews that show everything from simple button-based events to","sidebar":"tutorialSidebar"},"quick-starts/get-liveviewjs-repo":{"id":"quick-starts/get-liveviewjs-repo","title":"Download the Repo","description":"The fastest way to run the example or build your own LiveView is by downloading the LiveViewJS repo. This repo","sidebar":"tutorialSidebar"},"quick-starts/nodejs-build-first-liveview":{"id":"quick-starts/nodejs-build-first-liveview","title":"NodeJS - Build a LiveView","description":"Since you\'ve already downloaded the LiveViewJS repo, it should be easy to create a new","sidebar":"tutorialSidebar"},"quick-starts/nodejs-run-examples":{"id":"quick-starts/nodejs-run-examples","title":"NodeJS - Run the Examples","description":"LiveViewJS ships with over a dozen example LiveViews that show everything from simple button-based events to","sidebar":"tutorialSidebar"},"real-time-multi-player-pub-sub/example-pub-sub":{"id":"real-time-multi-player-pub-sub/example-pub-sub","title":"Example Pub/Sub LiveView","description":"We\'re going to extend our counter example from learning the LiveView API to","sidebar":"tutorialSidebar"},"real-time-multi-player-pub-sub/overview":{"id":"real-time-multi-player-pub-sub/overview","title":"Overview","description":"LiveViewJS natively supports real-time, multi-player user experiences. This is because LiveViewJS (and Phoenix","sidebar":"tutorialSidebar"},"user-events-slash-bindings/additional-bindings":{"id":"user-events-slash-bindings/additional-bindings","title":"Additional Bindings","description":"There are additional bindings outside of the four main bindings for User Events that you will find extremely useful and","sidebar":"tutorialSidebar"},"user-events-slash-bindings/bindings-table":{"id":"user-events-slash-bindings/bindings-table","title":"Phoenix LiveView Bindings","description":"Here is a table of all the bindings available in Phoenix LiveView and whether they are available in LiveViewJS.","sidebar":"tutorialSidebar"},"user-events-slash-bindings/overview":{"id":"user-events-slash-bindings/overview","title":"User Events / Bindings","description":"Four main User Events","sidebar":"tutorialSidebar"},"user-events-slash-bindings/rate-limiting-bindings":{"id":"user-events-slash-bindings/rate-limiting-bindings","title":"Rate Limiting Bindings","description":"Deboucing and throttling user events is a very common need and to support these use-cases there are the following","sidebar":"tutorialSidebar"},"webserver-integration/liveview-server-adaptor":{"id":"webserver-integration/liveview-server-adaptor","title":"LiveViewServerAdaptor","description":"LiveViewJS provides an interface that you can implement to integrate with your webserver of choice. This interface is","sidebar":"tutorialSidebar"},"webserver-integration/overview":{"id":"webserver-integration/overview","title":"Webserver Integration","description":"Out of the box, LiveViewJS supports two webserver integrations:","sidebar":"tutorialSidebar"},"webserver-integration/support-webserver-x":{"id":"webserver-integration/support-webserver-x","title":"Support Webserver \\"X\\"","description":"If you want to use LiveViewJS with a webserver that is not supported out of the box, you can implement the","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/9578e4ed.8fdbb7d4.js b/docs/assets/js/9578e4ed.8fdbb7d4.js deleted file mode 100644 index fdab1a57..00000000 --- a/docs/assets/js/9578e4ed.8fdbb7d4.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8686],{3928:e=>{e.exports=JSON.parse('{"title":"Lifecycle of a LiveView","description":"More details on the LiveView lifecycle including diagrams \ud83d\udcd0","slug":"/category/lifecycle-of-a-liveview","permalink":"/docs/category/lifecycle-of-a-liveview","navigation":{"previous":{"title":"Raw `LiveViewSocket` API","permalink":"/docs/liveview-socket/raw-liveviewsocket-api"},"next":{"title":"Lifecycle of a LiveView","permalink":"/docs/lifecycle-of-a-liveview/intro"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/9a32815e.dcb2119a.js b/docs/assets/js/9a32815e.dcb2119a.js deleted file mode 100644 index 27ec2fe1..00000000 --- a/docs/assets/js/9a32815e.dcb2119a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5548],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>v});var r=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=r.createContext({}),u=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=u(n),v=i,w=d["".concat(s,".").concat(v)]||d[v]||p[v]||o;return n?r.createElement(w,a(a({ref:t},c),{},{components:n})):r.createElement(w,a({ref:t},c))}));function v(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var u=2;u<o;u++)a[u]=n[u];return r.createElement.apply(null,a)}return r.createElement.apply(null,n)}d.displayName="MDXCreateElement"},5915:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>p,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var r=n(7896),i=(n(2784),n(876));const o={sidebar_position:5},a="Deno - Build a LiveView",l={unversionedId:"quick-starts/deno-build-first-liveview",id:"quick-starts/deno-build-first-liveview",title:"Deno - Build a LiveView",description:"Since you've already downloaded the LiveViewJS repo, it should be easy to create a new",source:"@site/docs/02-quick-starts/deno-build-first-liveview.md",sourceDirName:"02-quick-starts",slug:"/quick-starts/deno-build-first-liveview",permalink:"/docs/quick-starts/deno-build-first-liveview",draft:!1,tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"Deno - Run the Examples",permalink:"/docs/quick-starts/deno-run-examples"},next:{title:"Anatomy of a LiveView",permalink:"/docs/category/anatomy-of-a-liveview"}},s={},u=[{value:"Create a new LiveView in Deno",id:"create-a-new-liveview-in-deno",level:2},{value:"Setup a new Route",id:"setup-a-new-route",level:2},{value:"Start the Oak Server",id:"start-the-oak-server",level:2},{value:"See the LiveView in Action",id:"see-the-liveview-in-action",level:2},{value:"Next Steps",id:"next-steps",level:2},{value:"Great start!",id:"great-start",level:2}],c={toc:u};function p(e){let{components:t,...o}=e;return(0,i.kt)("wrapper",(0,r.Z)({},c,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"deno---build-a-liveview"},"Deno - Build a LiveView"),(0,i.kt)("p",null,"Since you've already ",(0,i.kt)("a",{parentName:"p",href:"get-liveviewjs-repo"},"downloaded the ",(0,i.kt)("strong",{parentName:"a"},"LiveViewJS")," repo"),", it should be easy to create a new\nLiveView and add it to your webserver. Let's get started!"),(0,i.kt)("h2",{id:"create-a-new-liveview-in-deno"},"Create a new LiveView in Deno"),(0,i.kt)("p",null,"Since we are using Deno to serve our LiveViews, we'll create a new LiveView in the ",(0,i.kt)("inlineCode",{parentName:"p"},"packages/deno")," directory."),(0,i.kt)("p",null,"Use your favorite editor to create a new file ",(0,i.kt)("inlineCode",{parentName:"p"},"packages/deno/src/example/liveview/hello.ts")," and add the following code\nand hit save:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import { createLiveView, html } from "liveviewjs";\n\nexport const helloLiveView = createLiveView({\n render: () => html`Hello World!`,\n});\n')),(0,i.kt)("p",null,"Congratulations! You've just created your first LiveView! It doesn't do much yet but let's get it running in the\nbrowser."),(0,i.kt)("h2",{id:"setup-a-new-route"},"Setup a new Route"),(0,i.kt)("p",null,"Let's add a route to this LiveView to see it in our browser. Edit ",(0,i.kt)("inlineCode",{parentName:"p"},"packages/deno/src/example/index.ts")," and make\nthe following highlighted changes:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="packages/deno/src/example/index.ts" {3,7}',title:'"packages/deno/src/example/index.ts"',"{3,7}":!0},'...\nimport { pageRenderer, rootRenderer } from "./liveViewRenderers.ts";\nimport { helloLiveView } from "./liveviews/hello.ts";\n\n// map request paths to LiveViews\nconst lvRouter: LiveViewRouter = {\n "/hello": helloLiveView,\n "/autocomplete": autocompleteLiveView,\n...\n')),(0,i.kt)("p",null,"Great! We've now setup our new LiveView to be served at the ",(0,i.kt)("inlineCode",{parentName:"p"},"/hello")," path. Let's start the server and see it in action."),(0,i.kt)("h2",{id:"start-the-oak-server"},"Start the Oak Server"),(0,i.kt)("p",null,"Start up the Oak server in Deno:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"# start oak server\ndeno run --allow-net --allow-read --allow-write --allow-env --import-map=import_map.json src/example/index.ts\n")),(0,i.kt)("h2",{id:"see-the-liveview-in-action"},"See the LiveView in Action"),(0,i.kt)("p",null,"Point your browser to ",(0,i.kt)("a",{parentName:"p",href:"http://localhost:9001/hello"},"http://localhost:9001/hello")," and you should see something like the\nfollowing: ",(0,i.kt)("img",{alt:"LiveViewJS Hello World Screenshot",src:n(2579).Z,width:"1630",height:"632"})),(0,i.kt)("h2",{id:"next-steps"},"Next Steps"),(0,i.kt)("p",null,"Ok we got our first LiveView running but it isn't very interactive. Let's make it more interesting by adding a button\nthat toggles between using text and emojis to say hello. Update the ",(0,i.kt)("inlineCode",{parentName:"p"},"hello.ts")," file to the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="packages/deno/src/example/liveview/hello.ts"',title:'"packages/deno/src/example/liveview/hello.ts"'},'import { createLiveView, html } from "liveviewjs";\n\nexport const helloLiveView = createLiveView({\n mount: (socket) => {\n socket.assign({ useEmoji: false });\n },\n handleEvent(event, socket) {\n socket.assign({ useEmoji: !socket.context.useEmoji });\n },\n render: (context) => {\n const msg = context.useEmoji ? "\ud83d\udc4b \ud83c\udf0e" : "Hello World";\n return html`\n ${msg}\n <br />\n <button phx-click="toggle">Toggle Message</button>\n `;\n },\n});\n')),(0,i.kt)("p",null,"Stop the Deno server and run the same command again to start the server."),(0,i.kt)("p",null,"Now, when you refresh the page, you should see a button that toggles between using text and emojis to say hello. It\nshould look something like this:"),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"LiveViewJS Hello World Recording",src:n(1456).Z,width:"1630",height:"630"})),(0,i.kt)("h2",{id:"great-start"},"Great start!"),(0,i.kt)("p",null,"You've just created your first LiveView and added it to your webserver! There is a lot more to learn about\n",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS"),", but you are well on your way. We recommend you continue to the\n",(0,i.kt)("a",{parentName:"p",href:"/docs/category/anatomy-of-a-liveview"},"Anatomy of a LiveView")," section to start to learn more about how LiveViews work."))}p.isMDXComponent=!0},2579:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/liveviewjs_hello_liveview_deno-0f12c470d6af8952a736434974b93bfd.png"},1456:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/liveviewjs_hello_toggle_liveview_deno_rec-6770b13c7109764fe416a368f712f377.gif"}}]); \ No newline at end of file diff --git a/docs/assets/js/9b689780.e50ad980.js b/docs/assets/js/9b689780.e50ad980.js deleted file mode 100644 index db638df1..00000000 --- a/docs/assets/js/9b689780.e50ad980.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5723],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>v});var r=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function a(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,p=a(e,["components","mdxType","originalType","parentName"]),u=c(n),v=i,h=u["".concat(l,".").concat(v)]||u[v]||d[v]||o;return n?r.createElement(h,s(s({ref:t},p),{},{components:n})):r.createElement(h,s({ref:t},p))}));function v(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,s=new Array(o);s[0]=u;var a={};for(var l in t)hasOwnProperty.call(t,l)&&(a[l]=t[l]);a.originalType=e,a.mdxType="string"==typeof e?e:i,s[1]=a;for(var c=2;c<o;c++)s[c]=n[c];return r.createElement.apply(null,s)}return r.createElement.apply(null,n)}u.displayName="MDXCreateElement"},2738:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>a,toc:()=>c});var r=n(7896),i=(n(2784),n(876));const o={sidebar_position:4},s="LiveViewSocket API - Server Events",a={unversionedId:"liveview-socket/liveviewsocket-api-infos",id:"liveview-socket/liveviewsocket-api-infos",title:"LiveViewSocket API - Server Events",description:"Server events are important to connect LiveViews with asynchronous processes. For example, a LiveView may need to wait",source:"@site/docs/04-liveview-socket/liveviewsocket-api-infos.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/liveviewsocket-api-infos",permalink:"/docs/liveview-socket/liveviewsocket-api-infos",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket API - Push",permalink:"/docs/liveview-socket/liveviewsocket-api-push"},next:{title:"LiveViewSocket API - Uploads",permalink:"/docs/liveview-socket/liveviewsocket-api-uploads"}},l={},c=[{value:"LiveViewSocket Properties and Methods",id:"liveviewsocket-properties-and-methods",level:2},{value:"<code>sendInfo</code> Method",id:"sendinfo-method",level:2},{value:"<code>subscribe</code> Method",id:"subscribe-method",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveviewsocket-api---server-events"},"LiveViewSocket API - Server Events"),(0,i.kt)("p",null,"Server events are important to connect LiveViews with asynchronous processes. For example, a LiveView may need to wait\nfor a long database query or search service to complete before rendering the results. Or a LiveView may want to send\nupdates based on a webhook or action from another user."),(0,i.kt)("h2",{id:"liveviewsocket-properties-and-methods"},"LiveViewSocket Properties and Methods"),(0,i.kt)("p",null,"There are two LiveViewSocket API methods that help facilitate server events: | Name | Description | |---|---| |\n",(0,i.kt)("inlineCode",{parentName:"p"},"sendInfo(info: Info<TInfos>): void;"),' | Send an internal event (a.k.a "Info") to the LiveView\'s ',(0,i.kt)("inlineCode",{parentName:"p"},"handleInfo")," method | |\n",(0,i.kt)("inlineCode",{parentName:"p"},"subscribe(topic: string): Promise<void>;")," | Subscribe to the given topic using pub/sub. Events published to this topic\nwill be delivered to ",(0,i.kt)("inlineCode",{parentName:"p"},"handleInfo"),". |"),(0,i.kt)("h2",{id:"sendinfo-method"},(0,i.kt)("inlineCode",{parentName:"h2"},"sendInfo")," Method"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"socket.sendInfo")," enables a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveView")," to send message to itself which is useful for executing actions that are\nasynchronous. Messages sent via ",(0,i.kt)("inlineCode",{parentName:"p"},"socket.sendInfo")," are received by the ",(0,i.kt)("inlineCode",{parentName:"p"},"handleInfo")," method after the current render\nlifecycle has completed. (In other words, ",(0,i.kt)("inlineCode",{parentName:"p"},"handleInfo")," is called after the ",(0,i.kt)("inlineCode",{parentName:"p"},"render")," call which will result in another\n",(0,i.kt)("inlineCode",{parentName:"p"},"render")," after ",(0,i.kt)("inlineCode",{parentName:"p"},"handleInfo")," completes.)"),(0,i.kt)("p",null,"When creating your ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveView")," you can provide the typing for ",(0,i.kt)("inlineCode",{parentName:"p"},"TInfo"),' which describes the "shape" of the possible info\nmessages. e.g.'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// Define the MyContext, MyEvents, and MyInfo types\ntype MyContext = {query: string, loading: boolean, results: string[]};\ntype MyEvents = {type: "search", query: string};\ntype MyInfo = {type: "run_search", query: string} | {type: "refresh"};\n\n// Annotate the createLiveView function with the types\nconst myLiveView = createLiveView<MyContext, MyEvents, MyInfo>(\n handleEvent: (event, socket) => {\n ...\n if(event.type === "search" ) {\n // update the context with loading status and empty results so\n // that UI will be updated for user\n socket.assign({ loading: true, results: [], query: event.query });\n // send internal message to run the search process\n socket.sendInfo({ type: "run_search", query: event.query })\n }\n }\n ...\n handleInfo: (info, socket) => {\n if(info.type === "run_search") {\n const { query } = info;\n // run the search\n const results = searchService.run(query)\n // update the context with results which will update the UI\n socket.assign({ loading: false, results })\n }\n ...\n }\n ...\n)\n')),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"socket.sendInfo")," can take a type as a string for cases where there isn't additional information passed along with\nthe message."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// or send just the "type" as a string\nsocket.sendInfo("refresh");\n')),(0,i.kt)("h2",{id:"subscribe-method"},(0,i.kt)("inlineCode",{parentName:"h2"},"subscribe")," Method"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"socket.subscribe")," enables a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveView")," to subscribe to a topic using pub/sub. Events published to this topic will be\ndelivered to ",(0,i.kt)("inlineCode",{parentName:"p"},"handleInfo"),". This is useful for cases where a LiveView needs to receive updates from another process or\nuser."),(0,i.kt)("p",null,"You can provide the type annotation for messages you expect to receive from a pub/sub topic as well."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/9bbe50cd.dcc99ddb.js b/docs/assets/js/9bbe50cd.dcc99ddb.js deleted file mode 100644 index 3cc19a30..00000000 --- a/docs/assets/js/9bbe50cd.dcc99ddb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9080],{876:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var i=n(2784);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){o(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,i,o=function(e,t){if(null==e)return{};var n,i,o={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=i.createContext({}),l=function(e){var t=i.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=l(e.components);return i.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=l(n),d=o,v=m["".concat(c,".").concat(d)]||m[d]||p[d]||r;return n?i.createElement(v,a(a({ref:t},u),{},{components:n})):i.createElement(v,a({ref:t},u))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,a=new Array(r);a[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var l=2;l<r;l++)a[l]=n[l];return i.createElement.apply(null,a)}return i.createElement.apply(null,n)}m.displayName="MDXCreateElement"},5797:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>r,metadata:()=>s,toc:()=>l});var i=n(7896),o=(n(2784),n(876));const r={sidebar_position:3},a="LiveView API - `mount`",s={unversionedId:"anatomy-of-a-liveview/mount-details",id:"anatomy-of-a-liveview/mount-details",title:"LiveView API - `mount`",description:"mount is called by the LiveViewJS runtime when your LiveView is first mounted",source:"@site/docs/03-anatomy-of-a-liveview/mount-details.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/mount-details",permalink:"/docs/anatomy-of-a-liveview/mount-details",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"LiveView API",permalink:"/docs/anatomy-of-a-liveview/liveview-api"},next:{title:"LiveView API - `render`",permalink:"/docs/anatomy-of-a-liveview/render-details"}},c={},l=[{value:"<code>mount</code> Signature",id:"mount-signature",level:2}],u={toc:l};function p(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"liveview-api---mount"},"LiveView API - ",(0,o.kt)("inlineCode",{parentName:"h1"},"mount")),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"mount")," is called by the ",(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," runtime when your LiveView is first mounted\n(",(0,o.kt)("a",{parentName:"p",href:"/docs/lifecycle-of-a-liveview/intro"},"over HTTP and Websocket"),"). ",(0,o.kt)("inlineCode",{parentName:"p"},"mount")," is where you initialize the context (i.e.\nstate) of your LiveView (using ",(0,o.kt)("inlineCode",{parentName:"p"},"socket.assign"),") and otherwise configure the LiveView. The\n",(0,o.kt)("a",{parentName:"p",href:"/docs/webserver-integration/overview"},"webserver integrations")," automatically make session data available via the\n",(0,o.kt)("inlineCode",{parentName:"p"},"session")," which can be useful if you need to use data from the user's session. Don't worry about ",(0,o.kt)("inlineCode",{parentName:"p"},"params")," for now. We'll\ncover that later."),(0,o.kt)("h2",{id:"mount-signature"},(0,o.kt)("inlineCode",{parentName:"h2"},"mount")," Signature"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},"mount(\n socket: LiveViewSocket<TContext, TInfos>,\n session: Partial<SessionData>,\n params: LiveViewMountParams\n): void | Promise<void>;\n")),(0,o.kt)("p",null,"As you can see in the ",(0,o.kt)("inlineCode",{parentName:"p"},"counterLiveView.ts")," below, ",(0,o.kt)("inlineCode",{parentName:"p"},"mount")," initializes the ",(0,o.kt)("inlineCode",{parentName:"p"},"count")," to ",(0,o.kt)("inlineCode",{parentName:"p"},"0")," (and doesn't use the ",(0,o.kt)("inlineCode",{parentName:"p"},"session"),"\nor ",(0,o.kt)("inlineCode",{parentName:"p"},"params"),"):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="counterLiveView.ts" {9-12}',title:'"counterLiveView.ts"',"{9-12}":!0},'import { createLiveView, html } from "liveviewjs";\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const counterLiveView = createLiveView<\n { count: number }, // Define LiveView Context / State\n { type: "increment" } | { type: "decrement" } // Define LiveView Events\n>({\n // Setup / initialize the LiveView Context (i.e., set count to 0)\n mount: (socket) => {\n socket.assign({ count: 0 });\n },\n // Handle incoming increment and decrement events from User input\n handleEvent: (event, socket) => {\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n socket.assign({ count: count + 1 });\n break;\n case "decrement":\n socket.assign({ count: count - 1 });\n break;\n }\n },\n // Renders the Counter View based on the current Context / State\n render: (context) => {\n const { count } = context;\n return html`\n <div>\n <h1>Count is: ${count}</h1>\n <button phx-click="decrement">-</button>\n <button phx-click="increment">+</button>\n </div>\n `;\n },\n});\n')),(0,o.kt)("admonition",{type:"info"},(0,o.kt)("p",{parentName:"admonition"},"The ",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," is passed into all methods except for ",(0,o.kt)("inlineCode",{parentName:"p"},"render"),". ",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," is the swiss army knife of\nLiveViewJS. We will cover its ",(0,o.kt)("a",{parentName:"p",href:"/docs/liveview-socket/liveviewsocket-api"},"API in more detail")," shortly.")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/9c3a86b6.61f78c9a.js b/docs/assets/js/9c3a86b6.61f78c9a.js deleted file mode 100644 index 3aa40c27..00000000 --- a/docs/assets/js/9c3a86b6.61f78c9a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1440],{876:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>u});var n=r(2784);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){i(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,n,i=function(e,t){if(null==e)return{};var r,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},c=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},w={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},v=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),v=p(r),u=i,d=v["".concat(l,".").concat(u)]||v[u]||w[u]||o;return r?n.createElement(d,a(a({ref:t},c),{},{components:r})):n.createElement(d,a({ref:t},c))}));function u(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=r.length,a=new Array(o);a[0]=v;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var p=2;p<o;p++)a[p]=r[p];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}v.displayName="MDXCreateElement"},4640:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>w,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var n=r(7896),i=(r(2784),r(876));const o={sidebar_position:1},a="Webserver Integration",s={unversionedId:"webserver-integration/overview",id:"webserver-integration/overview",title:"Webserver Integration",description:"Out of the box, LiveViewJS supports two webserver integrations:",source:"@site/docs/12-webserver-integration/overview.md",sourceDirName:"12-webserver-integration",slug:"/webserver-integration/overview",permalink:"/docs/webserver-integration/overview",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Webserver Integrations",permalink:"/docs/category/webserver-integrations"},next:{title:"LiveViewServerAdaptor",permalink:"/docs/webserver-integration/liveview-server-adaptor"}},l={},p=[{value:"How the Integration Works",id:"how-the-integration-works",level:2},{value:"Recap of the Integration Points",id:"recap-of-the-integration-points",level:2}],c={toc:p};function w(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"webserver-integration"},"Webserver Integration"),(0,i.kt)("p",null,"Out of the box, ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," supports two webserver integrations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"ExpressJS (NodeJS)"),(0,i.kt)("li",{parentName:"ul"},"Oak (Deno)")),(0,i.kt)("p",null,"Both integrations are very similar and are based on the same core API and intgration points between the webserver and\nthe LiveViewJS library."),(0,i.kt)("h2",{id:"how-the-integration-works"},"How the Integration Works"),(0,i.kt)("p",null,'As we\'ve covered elsewhere, LiveViewJS handles both HTTP and Websocket connections for routes that are registered with\nit. It does this by providing "middleware" (for HTTP and websocket traffic) that is plugged into the webserver.'),(0,i.kt)("p",null,"This middleware knows how to handle the HTTP and websocket traffic for the routes that are registered with LiveViewJS."),(0,i.kt)("p",null,"Let's look at an example of how this works in ExpressJS."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// create our express app\nconst app = express();\n...\n// whatever other express setup you need to do\n...\n// setup our LiveViewJS routes\nconst router: LiveViewRouter = {\n ...\n "/hello": helloLiveView,\n ...\n};\n...\n// initialize the NodeJS LiveViewServer\nconst liveView = new NodeExpressLiveViewServer(\n router,\n htmlPageTemplate, // HTML template for all liveviews\n { title: "Express Demo", suffix: " \xb7 LiveViewJS" }, // live tag options\n);\n\n// setup the LiveViewJS HTTP middleware\napp.use(liveView.httpMiddleware());\n\n\n// configure express to handle both http and websocket requests\nconst httpServer = new Server();\nconst wsServer = new WebSocketServer({\n server: httpServer,\n});\n\n// send http requests to the express app\nhttpServer.on("request", app);\n\n// setup the LiveViewJS websocket middleware\nconst liveViewWsMiddleware = liveView.wsMiddleware();\nliveViewWsMiddleware(wsServer);\n...\n')),(0,i.kt)("h2",{id:"recap-of-the-integration-points"},"Recap of the Integration Points"),(0,i.kt)("p",null,"Essentially, we do some LiveViewJS configuration, then we plug the LiveViewJS middleware into the webserver and\nwebsocket server."),(0,i.kt)("p",null,"When traffic comes in, the webserver will pass the request to the LiveViewJS middleware. The middleware checks if the\nrequest is for a LiveViewJS route. If it is, it will handle the request. If it isn't, it will pass the request to the\nnext middleware in the chain."),(0,i.kt)("p",null,"Let's look at the integration points in more detail in the next sections."))}w.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/9e4087bc.7cfaacea.js b/docs/assets/js/9e4087bc.7cfaacea.js deleted file mode 100644 index 976dc64f..00000000 --- a/docs/assets/js/9e4087bc.7cfaacea.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3608],{3008:(e,t,a)=>{a.r(t),a.d(t,{default:()=>o});var r=a(2784),l=a(9817),n=a(1077),c=a(328),i=a(9991);function m(e){let{year:t,posts:a}=e;return r.createElement(r.Fragment,null,r.createElement("h3",null,t),r.createElement("ul",null,a.map((e=>r.createElement("li",{key:e.metadata.date},r.createElement(l.Z,{to:e.metadata.permalink},e.metadata.formattedDate," - ",e.metadata.title))))))}function s(e){let{years:t}=e;return r.createElement("section",{className:"margin-vert--lg"},r.createElement("div",{className:"container"},r.createElement("div",{className:"row"},t.map(((e,t)=>r.createElement("div",{key:t,className:"col col--4 margin-vert--lg"},r.createElement(m,e)))))))}function o(e){let{archive:t}=e;const a=(0,n.I)({id:"theme.blog.archive.title",message:"Archive",description:"The page & hero title of the blog archive page"}),l=(0,n.I)({id:"theme.blog.archive.description",message:"Archive",description:"The page & hero description of the blog archive page"}),m=function(e){const t=e.reduceRight(((e,t)=>{var a;const r=t.metadata.date.split("-")[0],l=null!=(a=e.get(r))?a:[];return e.set(r,[t,...l])}),new Map);return Array.from(t,(e=>{let[t,a]=e;return{year:t,posts:a}}))}(t.blogPosts);return r.createElement(r.Fragment,null,r.createElement(c.d,{title:a,description:l}),r.createElement(i.Z,null,r.createElement("header",{className:"hero hero--primary"},r.createElement("div",{className:"container"},r.createElement("h1",{className:"hero__title"},a),r.createElement("p",{className:"hero__subtitle"},l))),r.createElement("main",null,m.length>0&&r.createElement(s,{years:m}))))}}}]); \ No newline at end of file diff --git a/docs/assets/js/a49e487e.d097d48c.js b/docs/assets/js/a49e487e.d097d48c.js deleted file mode 100644 index 5daf47a0..00000000 --- a/docs/assets/js/a49e487e.d097d48c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1037],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var i=n(2784);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?s(Object(n),!0).forEach((function(t){r(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):s(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function a(e,t){if(null==e)return{};var n,i,r=function(e,t){if(null==e)return{};var n,i,r={},s=Object.keys(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=i.createContext({}),l=function(e){var t=i.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=l(e.components);return i.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,s=e.originalType,c=e.parentName,p=a(e,["components","mdxType","originalType","parentName"]),d=l(n),m=r,h=d["".concat(c,".").concat(m)]||d[m]||u[m]||s;return n?i.createElement(h,o(o({ref:t},p),{},{components:n})):i.createElement(h,o({ref:t},p))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var s=n.length,o=new Array(s);o[0]=d;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a.mdxType="string"==typeof e?e:r,o[1]=a;for(var l=2;l<s;l++)o[l]=n[l];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}d.displayName="MDXCreateElement"},7797:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>u,frontMatter:()=>s,metadata:()=>a,toc:()=>l});var i=n(7896),r=(n(2784),n(876));const s={sidebar_position:1},o="Security Topics",a={unversionedId:"misc/security-topics",id:"misc/security-topics",title:"Security Topics",description:"Here are some security topics to consider when using LiveViewJS.",source:"@site/docs/13-misc/security-topics.md",sourceDirName:"13-misc",slug:"/misc/security-topics",permalink:"/docs/misc/security-topics",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Miscellaneous",permalink:"/docs/category/miscellaneous"},next:{title:"Live Components",permalink:"/docs/misc/livecomponents"}},c={},l=[{value:"Authenticity Tokens / CSRF Protection",id:"authenticity-tokens--csrf-protection",level:2},{value:"Websocket Join Authenticity",id:"websocket-join-authenticity",level:3},{value:"LiveViewJS Forms Authenticity",id:"liveviewjs-forms-authenticity",level:3},{value:"Session Data",id:"session-data",level:2},{value:"Default SerDe uses JWT",id:"default-serde-uses-jwt",level:3},{value:"Please Ask Questions \ud83c\udf81",id:"please-ask-questions-",level:2}],p={toc:l};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"security-topics"},"Security Topics"),(0,r.kt)("p",null,"Here are some security topics to consider when using LiveViewJS."),(0,r.kt)("h2",{id:"authenticity-tokens--csrf-protection"},"Authenticity Tokens / CSRF Protection"),(0,r.kt)("p",null,"We use CSRF tokens (a.k.a. Authenticity Tokens) to protect against Cross-Site Request Forgery (CSRF) attacks. CSRF\nattacks are a type of malicious exploit where unauthorized commands are performed on behalf of an authenticated user."),(0,r.kt)("h3",{id:"websocket-join-authenticity"},"Websocket Join Authenticity"),(0,r.kt)("p",null,"Every LiveView page load embeds a CSRF token in the page header in a ",(0,r.kt)("inlineCode",{parentName:"p"},"meta")," tag named ",(0,r.kt)("inlineCode",{parentName:"p"},"csrf-token"),". This token is\ncreated by ",(0,r.kt)("strong",{parentName:"p"},"LiveViewJS")," when the HTML page is initially rendered. The ",(0,r.kt)("strong",{parentName:"p"},"LiveViewJS")," client-side code automatically\npulls in that token and sends it to the server as part of the websocket join phase where it is used to verify that the\nrequest is legitimate."),(0,r.kt)("h3",{id:"liveviewjs-forms-authenticity"},"LiveViewJS Forms Authenticity"),(0,r.kt)("p",null,"LiveViewJS expects forms to have an input named ",(0,r.kt)("inlineCode",{parentName:"p"},"_csrf_token"),". This input field is automatically added to forms by the\n",(0,r.kt)("inlineCode",{parentName:"p"},"form_for")," helper and populated by the csrf token passed in during join. If you don't use ",(0,r.kt)("inlineCode",{parentName:"p"},"form_for")," you can add a\nhidden input with the name ",(0,r.kt)("inlineCode",{parentName:"p"},"_csrf_token")," and populate it yourself. When the form is submitted, the ",(0,r.kt)("inlineCode",{parentName:"p"},"_csrf_token")," input\nfield is sent to the server along with the form data. The ",(0,r.kt)("strong",{parentName:"p"},"LiveViewJS")," server verifies that the submitted CSRF token\nmatches the CSRF token from the page header. If they do not match, the request is rejected. ",(0,r.kt)("strong",{parentName:"p"},"If the CSRF token is\nmissing, the server prints out a warning but allows the form submission to continue.")),(0,r.kt)("h2",{id:"session-data"},"Session Data"),(0,r.kt)("p",null,"Part of the webserver integration is being able to pass any session data from the HTTP session to the LiveView websocket\nsession. ",(0,r.kt)("strong",{parentName:"p"},"LiveViewJS")," allows each webserver integration to implement a ",(0,r.kt)("inlineCode",{parentName:"p"},"getSessionData")," method that returns a JSON\nobject containing the session data. ",(0,r.kt)("strong",{parentName:"p"},"LiveViewJS")," then uses the webserver integration's serializer/deserializer (a.k.a.\n",(0,r.kt)("inlineCode",{parentName:"p"},"SerDe"),") to serialize this data to be passed to the server as part of the websocket join. The ",(0,r.kt)("strong",{parentName:"p"},"LiveViewJS")," server then\ndeserializes the session data and makes it available to the LiveView via the ",(0,r.kt)("inlineCode",{parentName:"p"},"session")," property in ",(0,r.kt)("inlineCode",{parentName:"p"},"mount"),"."),(0,r.kt)("h3",{id:"default-serde-uses-jwt"},"Default SerDe uses JWT"),(0,r.kt)("p",null,"The default SerDe implementation uses JWT to serialize and deserialize the session data. This means that the session\ndata is signed (which prevents tampering) but not encrypted. If you want to encrypt the session data, you will have to\nimplement your own SerDe."),(0,r.kt)("h2",{id:"please-ask-questions-"},"Please Ask Questions \ud83c\udf81"),(0,r.kt)("p",null,"If there is something you are concerned about regarding security and LiveViewJS, please add an issue to the\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/issues"},"LiveViewJS GitHub repo"),". We will do our best to answer your questions and\naddress any concerns you may have."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/a6aa9e1f.b9c01e8d.js b/docs/assets/js/a6aa9e1f.b9c01e8d.js deleted file mode 100644 index 37782eac..00000000 --- a/docs/assets/js/a6aa9e1f.b9c01e8d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3089],{9956:(e,t,a)=>{a.r(t),a.d(t,{default:()=>u});var n=a(2784),l=a(6277),r=a(7614),i=a(328),o=a(211),s=a(3485),m=a(2156),c=a(4390),g=a(1852);function p(e){const{metadata:t}=e,{siteConfig:{title:a}}=(0,r.Z)(),{blogDescription:l,blogTitle:o,permalink:s}=t,m="/"===s?a:o;return n.createElement(n.Fragment,null,n.createElement(i.d,{title:m,description:l}),n.createElement(c.Z,{tag:"blog_posts_list"}))}function d(e){const{metadata:t,items:a,sidebar:l}=e;return n.createElement(s.Z,{sidebar:l},n.createElement(g.Z,{items:a}),n.createElement(m.Z,{metadata:t}))}function u(e){return n.createElement(i.FG,{className:(0,l.Z)(o.k.wrapper.blogPages,o.k.page.blogListPage)},n.createElement(p,e),n.createElement(d,e))}},2156:(e,t,a)=>{a.d(t,{Z:()=>i});var n=a(2784),l=a(1077),r=a(7066);function i(e){const{metadata:t}=e,{previousPage:a,nextPage:i}=t;return n.createElement("nav",{className:"pagination-nav","aria-label":(0,l.I)({id:"theme.blog.paginator.navAriaLabel",message:"Blog list page navigation",description:"The ARIA label for the blog pagination"})},a&&n.createElement(r.Z,{permalink:a,title:n.createElement(l.Z,{id:"theme.blog.paginator.newerEntries",description:"The label used to navigate to the newer blog posts page (previous page)"},"Newer Entries")}),i&&n.createElement(r.Z,{permalink:i,title:n.createElement(l.Z,{id:"theme.blog.paginator.olderEntries",description:"The label used to navigate to the older blog posts page (next page)"},"Older Entries"),isNext:!0}))}},1852:(e,t,a)=>{a.d(t,{Z:()=>i});var n=a(2784),l=a(1375),r=a(4646);function i(e){let{items:t,component:a=r.Z}=e;return n.createElement(n.Fragment,null,t.map((e=>{let{content:t}=e;return n.createElement(l.n,{key:t.metadata.permalink,content:t},n.createElement(a,null,n.createElement(t,null)))})))}}}]); \ No newline at end of file diff --git a/docs/assets/js/a7023ddc.fbeef8ab.js b/docs/assets/js/a7023ddc.fbeef8ab.js deleted file mode 100644 index dacdc59a..00000000 --- a/docs/assets/js/a7023ddc.fbeef8ab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1713],{3457:l=>{l.exports=JSON.parse('[{"label":"facebook","permalink":"/blog/tags/facebook","count":1},{"label":"hello","permalink":"/blog/tags/hello","count":2},{"label":"docusaurus","permalink":"/blog/tags/docusaurus","count":4},{"label":"hola","permalink":"/blog/tags/hola","count":1}]')}}]); \ No newline at end of file diff --git a/docs/assets/js/a7e77012.33ce6ba3.js b/docs/assets/js/a7e77012.33ce6ba3.js deleted file mode 100644 index a80ca9dc..00000000 --- a/docs/assets/js/a7e77012.33ce6ba3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[450],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(2784);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){o(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},h={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=c(n),u=o,f=d["".concat(l,".").concat(u)]||d[u]||h[u]||r;return n?a.createElement(f,i(i({ref:t},p),{},{components:n})):a.createElement(f,i({ref:t},p))}));function u(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var r=n.length,i=new Array(r);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var c=2;c<r;c++)i[c]=n[c];return a.createElement.apply(null,i)}return a.createElement.apply(null,n)}d.displayName="MDXCreateElement"},9409:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>h,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var a=n(7896),o=(n(2784),n(876));const r={sidebar_position:7},i="Raw `LiveViewSocket` API",s={unversionedId:"liveview-socket/raw-liveviewsocket-api",id:"liveview-socket/raw-liveviewsocket-api",title:"Raw `LiveViewSocket` API",description:"For your reference, below is the raw Typescript for the LiveViewSocket interface (copied from",source:"@site/docs/04-liveview-socket/raw-liveviewsocket-api.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/raw-liveviewsocket-api",permalink:"/docs/liveview-socket/raw-liveviewsocket-api",draft:!1,tags:[],version:"current",sidebarPosition:7,frontMatter:{sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket API - Misc",permalink:"/docs/liveview-socket/liveviewsocket-api-misc"},next:{title:"Lifecycle of a LiveView",permalink:"/docs/category/lifecycle-of-a-liveview"}},l={},c=[],p={toc:c};function h(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"raw-liveviewsocket-api"},"Raw ",(0,o.kt)("inlineCode",{parentName:"h1"},"LiveViewSocket")," API"),(0,o.kt)("p",null,"For your reference, below is the raw Typescript for the ",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," interface (copied from\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/blob/main/packages/core/src/server/socket/liveSocket.ts"},"here"),"):"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="packages/core/src/server/socket/liveSocket.ts"',title:'"packages/core/src/server/socket/liveSocket.ts"'},"/**\n * Main interface to update state, interact, message, and otherwise\n * manage the lifecycle of a `LiveView`.\n *\n * The `LiveView` API (i.e., `mount`, `handleParams`, `handleInfo`, `handleEvent`)\n * are all passed `LiveViewSocket` which provide access to the current `LiveView`\n * context (via `context`) as well as various methods update the `LiveView` including\n * `assign` which updates the `LiveView`'s context (i.e., state).\n */\nexport interface LiveViewSocket<TContext extends LiveContext = AnyLiveContext, TInfos extends LiveInfo = AnyLiveInfo> {\n /**\n * The id of the `LiveView`\n */\n readonly id: string;\n /**\n * Whether the websocket is connected.\n * true if connected to a websocket, false for http request\n */\n readonly connected: boolean;\n /**\n * The current context (i.e., state) of the `LiveView`\n */\n readonly context: TContext;\n /**\n * `assign` is used to update the context (i.e., state) of the `LiveComponent`\n * @param context a `Partial` of the LiveView's context to update\n */\n assign(context: Partial<TContext>): void;\n /**\n * Marks any set properties as temporary and will be reset to the given\n * value after the next render cycle. Typically used to ensure large but\n * infrequently updated values are not kept in memory.\n *\n * @param context a partial of the context that should be temporary and the value to reset it to\n */\n tempAssign(context: Partial<TContext>): void;\n /**\n * Updates the `<title>` tag of the `LiveView` page. Requires using the\n * `live_title` helper in rendering the page.\n *\n * @param newPageTitle the new text value of the page - note the prefix and suffix will not be changed\n */\n pageTitle(newPageTitle: string): void;\n /**\n * Pushes an event from the server to the client. Requires a\n * client `Hook` to be defined and to be listening for the event\n * via `this.handleEvent` callback.\n *\n * @param pushEvent the event to push to the client\n */\n pushEvent(pushEvent: AnyLivePushEvent): void;\n /**\n * Updates the LiveView's browser URL with the given path and query parameters.\n *\n * @param path the path whose query params are being updated\n * @param params the query params to update the path with\n * @param replaceHistory whether to replace the current history entry or push a new one (defaults to false)\n */\n pushPatch(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;\n /**\n * Shutdowns the current `LiveView`and loads another `LiveView`in its place\n * without reloading the whole page (i.e., making a full HTTP request). Can be\n * used to remount the current `LiveView`if need be. Use `pushPatch` to update the\n * current `LiveView`without unloading and remounting.\n *\n * @param path the path whose query params are being updated\n * @param params the query params to update the path with\n * @param replaceHistory whether to replace the current history entry or push a new one (defaults to false)\n */\n pushRedirect(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;\n /**\n * Add flash to the socket for a given key and value.\n * @param key the key to add the flash to\n * @param value the flash value\n */\n putFlash(key: string, value: string): Promise<void>;\n /**\n * Runs the given function on the given interval until this `LiveView` is\n * unloaded.\n *\n * @param fn the function to run on the interval\n * @param intervalMillis the interval to run the function on in milliseconds\n */\n repeat(fn: () => void, intervalMillis: number): void;\n /**\n * Send an internal event (a.k.a \"Info\") to the LiveView's `handleInfo` method\n *\n * @param event the event to send to `handleInfo`\n */\n sendInfo(info: Info<TInfos>): void;\n /**\n * Subscribe to the given topic using pub/sub. Events published to this topic\n * will be delivered to `handleInfo`.\n *\n * @param topic the topic to subscribe this `LiveView`to\n */\n subscribe(topic: string): Promise<void>;\n\n /**\n * Allows file uploads for the given `LiveView`and configures the upload\n * options (filetypes, size, etc).\n * @param name the name of the upload\n * @param options the options for the upload (optional)\n */\n allowUpload(name: string, options?: UploadConfigOptions): Promise<void>;\n\n /**\n * Cancels the file upload for a given UploadConfig by config name and file ref.\n * @param name the name of the upload from which to cancel\n * @param ref the ref of the upload entry to cancel\n */\n cancelUpload(configName: string, ref: string): Promise<void>;\n\n /**\n * Consume the uploaded files for a given UploadConfig (by name). This\n * should only be called after the form's \"save\" event has occurred which\n * guarantees all the files for the upload have been fully uploaded.\n * @param name the name of the upload from which to consume\n * @param fn the callback to run for each entry\n * @returns an array of promises based on the return type of the callback function\n * @throws if any of the entries are not fully uploaded (i.e., completed)\n */\n consumeUploadedEntries<T>(\n configName: string,\n fn: (meta: ConsumeUploadedEntriesMeta, entry: UploadEntry) => Promise<T>\n ): Promise<T[]>;\n\n /**\n * Returns two sets of files that are being uploaded, those `completed` and\n * those `inProgress` for a given UploadConfig (by name). Unlike `consumeUploadedEntries`,\n * this does not require the form's \"save\" event to have occurred and will not\n * throw if any of the entries are not fully uploaded.\n * @param name the name of the upload from which to get the entries\n * @returns an object with `completed` and `inProgress` entries\n */\n uploadedEntries(configName: string): Promise<{\n completed: UploadEntry[];\n inProgress: UploadEntry[];\n }>;\n}\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/a80da1cf.3f59ef08.js b/docs/assets/js/a80da1cf.3f59ef08.js deleted file mode 100644 index 36bdb27b..00000000 --- a/docs/assets/js/a80da1cf.3f59ef08.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3205],{4863:s=>{s.exports=JSON.parse('{"label":"docusaurus","permalink":"/blog/tags/docusaurus","allTagsPath":"/blog/tags","count":4}')}}]); \ No newline at end of file diff --git a/docs/assets/js/ae0c09be.4134591f.js b/docs/assets/js/ae0c09be.4134591f.js deleted file mode 100644 index 113bddd0..00000000 --- a/docs/assets/js/ae0c09be.4134591f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5024],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var i=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),h=a,v=u["".concat(l,".").concat(h)]||u[h]||d[h]||r;return n?i.createElement(v,o(o({ref:t},p),{},{components:n})):i.createElement(v,o({ref:t},p))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,o[1]=s;for(var c=2;c<r;c++)o[c]=n[c];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}u.displayName="MDXCreateElement"},5351:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>s,toc:()=>c});var i=n(7896),a=(n(2784),n(876));const r={sidebar_position:1},o="Introduction",s={unversionedId:"overview/introduction",id:"overview/introduction",title:"Introduction",description:'LiveViewJS is an open-source framework for "LiveView"-based, full-stack applications in NodeJS and Deno.',source:"@site/docs/01-overview/introduction.md",sourceDirName:"01-overview",slug:"/overview/introduction",permalink:"/docs/overview/introduction",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Overview",permalink:"/docs/category/overview"},next:{title:"LiveView Paradigm",permalink:"/docs/overview/paradigm"}},l={},c=[{value:"What is a LiveView?",id:"what-is-a-liveview",level:2},{value:"LiveView is already proven technology",id:"liveview-is-already-proven-technology",level:2},{value:"Advantages",id:"advantages",level:2},{value:"Disadvantages",id:"disadvantages",level:2},{value:"How is this different than Phoenix LiveView?",id:"how-is-this-different-than-phoenix-liveview",level:2},{value:"Reach more developers",id:"reach-more-developers",level:3}],p={toc:c};function d(e){let{components:t,...r}=e;return(0,a.kt)("wrapper",(0,i.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"introduction"},"Introduction"),(0,a.kt)("p",null,'LiveViewJS is an open-source framework for "LiveView"-based, full-stack applications in NodeJS and Deno.'),(0,a.kt)("p",null,"The LiveView approach allows developers to build applications with rich user experiences like React, Vue, etc, but ",(0,a.kt)("strong",{parentName:"p"},"with\nfar less code and complexity and far more speed and efficiency"),"."),(0,a.kt)("h2",{id:"what-is-a-liveview"},"What is a LiveView?"),(0,a.kt)("p",null,"A LiveView is a server-rendered HTML page that connects to the server via a persistent web socket. The web socket allows\nthe client to send user events to the server and the server to send diffs in response. ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," is a LiveView\nframework that handles the complex part of this (handling websockets, diffing changes, applying DOM updates, etc.) so that\nyou can focus on building your application."),(0,a.kt)("p",null,'Here\'s the typical "counter" example written as a LiveView in ',(0,a.kt)("strong",{parentName:"p"},"LiveViewJS"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { createLiveView, html } from "liveviewjs";\n\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const counterLiveView = createLiveView<\n { count: number }, // Define LiveView Context (a.k.a state)\n { type: "increment" } | { type: "decrement" } // Define LiveView Events\n>({\n mount: (socket) => {\n // init state, set count to 0\n socket.assign({ count: 0 });\n },\n handleEvent: (event, socket) => {\n // handle increment and decrement events\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n socket.assign({ count: count + 1 });\n break;\n case "decrement":\n socket.assign({ count: count - 1 });\n break;\n }\n },\n render: (context) => {\n // render the view based on the state\n const { count } = context;\n return html`\n <div>\n <h1>Count is: ${count}</h1>\n <button phx-click="decrement">-</button>\n <button phx-click="increment">+</button>\n </div>\n `;\n },\n});\n')),(0,a.kt)("p",null,"And here is what that LiveView looks like in a browser:\n",(0,a.kt)("img",{alt:"LiveView Counter Example Screen Recording",src:n(4766).Z,width:"1814",height:"1162"})),(0,a.kt)("p",null,'Yes, it "looks" like a React/Vue/Svelt UI but the main differences are:'),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"This page was first rendered as plain HTML (not a bundle of JS)"),(0,a.kt)("li",{parentName:"ul"},"The client is automatically connected to a server via a websocket"),(0,a.kt)("li",{parentName:"ul"},"The click events are automatically shipped to the server"),(0,a.kt)("li",{parentName:"ul"},"The server then runs the business logic to update the state"),(0,a.kt)("li",{parentName:"ul"},"Using the new state, the server then renders a new view and calculates any diffs"),(0,a.kt)("li",{parentName:"ul"},"Those diffs are shipped back to the client, where they are automatically applied")),(0,a.kt)("p",null,"Pretty cool eh? We think so too! While this counter LiveView isn't particularly useful, it gives you a quick intro to how\nLiveViews work and what they look like both as code and in the browser. We've got a lot more to show you about\n",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," including: built-in real-time / multi-player support, built-in form validation with changesets, built-in\nfile uploads with image previews and drag and drop support, and more!"),(0,a.kt)("p",null,"But first, a bit more about LiveViews..."),(0,a.kt)("h2",{id:"liveview-is-already-proven-technology"},"LiveView is already proven technology"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html"},"Phoenix LiveView")," is already extremely popular in the\nElixir community and has been used to build production applications for years. It powers delightful user experiences and is battle-tested in terms of performance, reliability, and developer productivity."),(0,a.kt)("p",null,"Here is a quote from the author and inventor of LiveView, ",(0,a.kt)("a",{parentName:"p",href:"http://chrismccord.com/"},"Chris McCord")," from\n",(0,a.kt)("a",{parentName:"p",href:"https://fly.io/blog/how-we-got-to-liveview/"},'"how we got to liveview"'),":"),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"LiveView strips away layers of abstraction, because it solves both the client and server in a single abstraction. HTTP\nalmost entirely falls away. No more REST. No more JSON. No GraphQL APIs, controllers, serializers, or resolvers. You\njust write HTML templates, and a stateful process synchronizes it with the browser, updating it only when needed.")),(0,a.kt)("h2",{id:"advantages"},"Advantages"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"Faster way to build rich, full-stack application")," - LiveView abstracts away the complexity of client/server\ncommunication, state management, and synchronization to make it simple and fast to build rich, server-connected user\nexperiences. See ",(0,a.kt)("a",{parentName:"li",href:"/docs/overview/paradigm"},"LiveView Paradigm")," for more details."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"Real-time and multi-player built-in")," - Broadcasting updates to single or multiple clients is natively supported in\nLiveViewJS. Building features like chat, presence, and real-time dashboards are all supported by LiveView's Pub/Sub.\nWe ship with support for Redis (NodeJS) and BroadcastChannels (Deno)."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},'Extremely fast "first paint"'),' - No massive JS bundle downloads, virtual DOM, "hydration", data-fetching, or the\nlike. LiveViews are first rendered statically as part of a normal HTTP response. This means "first-paint" is extremely\nfast.'),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"Extremely fast user-initiated updates")," - LiveView automatically maintains a persistent socket connection between\nclient and server. User events are sent to the server and optimized diffs are sent back to the client. All this\nhappens extremely fast and transparently to user and developer."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"No need to build REST or GraphQL APIs")," - Full-stack applications usually have front-end and back-end code bases\nadding complexity, effort, and context switching. The LiveView paradigm merges front-end and back-end\ninto a single abstraction layer which greatly increases productivity."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"Robust, battle-tested browser libraries")," - ",(0,a.kt)("strong",{parentName:"li"},"LiveViewJS")," uses the exact same browser libraries as Phoenix\nLiveView which has 10s of thousands of production applications serving millions of users. This isn't some new,\nunproven technology."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"No client/server state synchronization challenges")," - State synchronization between client and server is super\ncomplex for traditional SPAs. LiveViews do not have to worry about the challenges inherent in state synchronization\nbecause the server is always the source of truth and client updates are pushed to the client."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"No client-side routing")," - No need to use a library like React Router or Vue Router. LiveViews route like multi-page\napplications which is handled automatically by the browser and server. (Another major complication rendered\nunnecessary.)"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"No component libraries (or Storybook) required")," - Traditional SPAs require teams to adopt, build or buy and then\nmaintain, and customize a component library and use something like ",(0,a.kt)("a",{parentName:"li",href:"https://storybook.js.org/"},"Storybook"),". LiveView\nsimplifies this greatly with server-side HTML templates.")),(0,a.kt)("h2",{id:"disadvantages"},"Disadvantages"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"Not a drop-in replacement for traditional SPAs")," - LiveView is a new paradigm and is not a drop-in replacement for\ntraditional SPA frameworks like React or Vue. It is a new way of building full-stack applications."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"Not a great solution for pure static sites")," - Static sites that do not have user events or don't update often are\nnot a great fit for LiveView.")),(0,a.kt)("h2",{id:"how-is-this-different-than-phoenix-liveview"},"How is this different than Phoenix LiveView?"),(0,a.kt)("p",null,"The Phoenix project's backend is written in Elixir and runs on the ErlangVM. ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," is a protocol compliant,\nimplementation of Phoenix LiveView but written in Typescript and runs on NodeJS and Deno. ",(0,a.kt)("strong",{parentName:"p"},"We want to bring the magic\nand productivity of LiveView to the NodeJS and Deno ecosystems")," and are obviously huge fans of Phoenix LiveView and the\nteam that invented it. We believe in it so much that we think more developers should have access to the programming\nparadigm it enables."),(0,a.kt)("h3",{id:"reach-more-developers"},"Reach more developers"),(0,a.kt)("p",null,"Unfortunately, Elixir only represents\n",(0,a.kt)("a",{parentName:"p",href:"https://survey.stackoverflow.co/2022/#section-most-popular-technologies-programming-scripting-and-markup-languages"},"about 2%"),"\nof the programming language market share. We believe that ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," will help bring the productivity and magic of\nLiveView to the\n",(0,a.kt)("a",{parentName:"p",href:"https://survey.stackoverflow.co/2022/#section-most-popular-technologies-programming-scripting-and-markup-languages"},"65% of developers"),"\nthat use Javascript (and Typescript)."))}d.isMDXComponent=!0},4766:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/liveviewjs_counter_liveview_rec-e37b9d6de9defd8db8053c898ef0a326.gif"}}]); \ No newline at end of file diff --git a/docs/assets/js/b2b675dd.aad3112b.js b/docs/assets/js/b2b675dd.aad3112b.js deleted file mode 100644 index 2b05112e..00000000 --- a/docs/assets/js/b2b675dd.aad3112b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[533],{8017:e=>{e.exports=JSON.parse('{"permalink":"/blog","page":1,"postsPerPage":10,"totalPages":1,"totalCount":4,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/b2f554cd.15a03ba7.js b/docs/assets/js/b2f554cd.15a03ba7.js deleted file mode 100644 index 05043950..00000000 --- a/docs/assets/js/b2f554cd.15a03ba7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1477],{10:e=>{e.exports=JSON.parse('{"blogPosts":[{"id":"welcome","metadata":{"permalink":"/blog/welcome","source":"@site/blog/2021-08-26-welcome/index.md","title":"Welcome","description":"Docusaurus blogging features are powered by the","date":"2021-08-26T00:00:00.000Z","formattedDate":"August 26, 2021","tags":[{"label":"facebook","permalink":"/blog/tags/facebook"},{"label":"hello","permalink":"/blog/tags/hello"},{"label":"docusaurus","permalink":"/blog/tags/docusaurus"}],"readingTime":0.405,"hasTruncateMarker":false,"authors":[{"name":"Donnie Flood","title":"LiveViewJS Author","url":"https://github.com/floodfx","imageURL":"https://github.com/floodfx.png","key":"floodfx"}],"frontMatter":{"slug":"welcome","title":"Welcome","authors":["floodfx"],"tags":["facebook","hello","docusaurus"]},"nextItem":{"title":"MDX Blog Post","permalink":"/blog/mdx-blog-post"}},"content":"[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the\\n[blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).\\n\\nSimply add Markdown files (or folders) to the `blog` directory.\\n\\nRegular blog authors can be added to `authors.yml`.\\n\\nThe blog post date can be extracted from filenames, such as:\\n\\n- `2019-05-30-welcome.md`\\n- `2019-05-30-welcome/index.md`\\n\\nA blog post folder can be convenient to co-locate blog post images:\\n\\n![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg)\\n\\nThe blog supports tags as well!\\n\\n**And if you don\'t want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config."},{"id":"mdx-blog-post","metadata":{"permalink":"/blog/mdx-blog-post","source":"@site/blog/2021-08-01-mdx-blog-post.mdx","title":"MDX Blog Post","description":"Blog posts support Docusaurus Markdown features, such as MDX.","date":"2021-08-01T00:00:00.000Z","formattedDate":"August 1, 2021","tags":[{"label":"docusaurus","permalink":"/blog/tags/docusaurus"}],"readingTime":0.175,"hasTruncateMarker":false,"authors":[{"name":"Donnie Flood","title":"LiveViewJS Author","url":"https://github.com/floodfx","imageURL":"https://github.com/floodfx.png","key":"floodfx"}],"frontMatter":{"slug":"mdx-blog-post","title":"MDX Blog Post","authors":["floodfx"],"tags":["docusaurus"]},"prevItem":{"title":"Welcome","permalink":"/blog/welcome"},"nextItem":{"title":"Long Blog Post","permalink":"/blog/long-blog-post"}},"content":"Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).\\n\\n:::tip\\n\\nUse the power of React to create interactive blog posts.\\n\\n```js\\n<button onClick={() => alert(\\"button clicked!\\")}>Click me!</button>\\n```\\n\\n<button onClick={() => alert(\\"button clicked!\\")}>Click me!</button>\\n\\n:::"},{"id":"long-blog-post","metadata":{"permalink":"/blog/long-blog-post","source":"@site/blog/2019-05-29-long-blog-post.md","title":"Long Blog Post","description":"This is the summary of a very long blog post,","date":"2019-05-29T00:00:00.000Z","formattedDate":"May 29, 2019","tags":[{"label":"hello","permalink":"/blog/tags/hello"},{"label":"docusaurus","permalink":"/blog/tags/docusaurus"}],"readingTime":2.05,"hasTruncateMarker":true,"authors":[{"name":"Donnie Flood","title":"LiveViewJS Author","url":"https://github.com/floodfx","imageURL":"https://github.com/floodfx.png","key":"floodfx"}],"frontMatter":{"slug":"long-blog-post","title":"Long Blog Post","authors":"floodfx","tags":["hello","docusaurus"]},"prevItem":{"title":"MDX Blog Post","permalink":"/blog/mdx-blog-post"},"nextItem":{"title":"First Blog Post","permalink":"/blog/first-blog-post"}},"content":"This is the summary of a very long blog post,\\n\\nUse a `\x3c!--` `truncate` `--\x3e` comment to limit blog post size in the list view.\\n\\n\x3c!--truncate--\x3e\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet\\n\\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"},{"id":"first-blog-post","metadata":{"permalink":"/blog/first-blog-post","source":"@site/blog/2019-05-28-first-blog-post.md","title":"First Blog Post","description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum","date":"2019-05-28T00:00:00.000Z","formattedDate":"May 28, 2019","tags":[{"label":"hola","permalink":"/blog/tags/hola"},{"label":"docusaurus","permalink":"/blog/tags/docusaurus"}],"readingTime":0.12,"hasTruncateMarker":false,"authors":[{"name":"Donnie Flood","title":"LiveViewJS Author","url":"https://github.com/floodfx","imageURL":"https://github.com/floodfx.png","key":"floodfx"}],"frontMatter":{"slug":"first-blog-post","title":"First Blog Post","authors":["floodfx"],"tags":["hola","docusaurus"]},"prevItem":{"title":"Long Blog Post","permalink":"/blog/long-blog-post"}},"content":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"}]}')}}]); \ No newline at end of file diff --git a/docs/assets/js/b747b5f5.70f727bd.js b/docs/assets/js/b747b5f5.70f727bd.js deleted file mode 100644 index 3505f6ef..00000000 --- a/docs/assets/js/b747b5f5.70f727bd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7599],{3500:e=>{e.exports=JSON.parse('{"title":"Forms & Changesets","description":"How LiveViewJS makes form validation and submission easy with Changesets","slug":"/category/forms--changesets","permalink":"/docs/category/forms--changesets","navigation":{"previous":{"title":"Phoenix LiveView Bindings","permalink":"/docs/user-events-slash-bindings/bindings-table"},"next":{"title":"Overview","permalink":"/docs/forms-and-changesets/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/b7a0085b.997180ee.js b/docs/assets/js/b7a0085b.997180ee.js deleted file mode 100644 index ebf7c0ab..00000000 --- a/docs/assets/js/b7a0085b.997180ee.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6258],{876:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>f});var n=r(2784);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){o(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function l(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var d=n.createContext({}),p=function(e){var t=n.useContext(d),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},s=function(e){var t=p(e.components);return n.createElement(d.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,d=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),c=p(r),f=o,m=c["".concat(d,".").concat(f)]||c[f]||u[f]||i;return r?n.createElement(m,a(a({ref:t},s),{},{components:r})):n.createElement(m,a({ref:t},s))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=c;var l={};for(var d in t)hasOwnProperty.call(t,d)&&(l[d]=t[d]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var p=2;p<i;p++)a[p]=r[p];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}c.displayName="MDXCreateElement"},3132:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>a,default:()=>u,frontMatter:()=>i,metadata:()=>l,toc:()=>p});var n=r(7896),o=(r(2784),r(876));const i={sidebar_position:3},a="Built-in Drag and Drop",l={unversionedId:"file-upload/drag-and-drop",id:"file-upload/drag-and-drop",title:"Built-in Drag and Drop",description:"LiveViewJS ships with built-in support for drag and drop file uploads. It is incredibly easy to use. All you need to do",source:"@site/docs/08-file-upload/drag-and-drop.md",sourceDirName:"08-file-upload",slug:"/file-upload/drag-and-drop",permalink:"/docs/file-upload/drag-and-drop",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Built-in Image Preview",permalink:"/docs/file-upload/image-preview"},next:{title:"Upload Config Options",permalink:"/docs/file-upload/upload-config-options"}},d={},p=[{value:"Credit where credit is due",id:"credit-where-credit-is-due",level:2}],s={toc:p};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},s,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"built-in-drag-and-drop"},"Built-in Drag and Drop"),(0,o.kt)("p",null,"LiveViewJS ships with built-in support for drag and drop file uploads. It is incredibly easy to use. All you need to do\nis add a ",(0,o.kt)("inlineCode",{parentName:"p"},"<div />")," that has the ",(0,o.kt)("inlineCode",{parentName:"p"},"phx-drop-target")," attribute set to the upload config ref you want to target. For\nexample, if you want to allow users to drag and drop files into a ",(0,o.kt)("inlineCode",{parentName:"p"},"photos")," upload config, you would do the following:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-ts"},'...\nrender: (context, meta) => {\n ...\n <div phx-drop-target="${meta.uploads.photos.ref}">\n Drop files here\n </div>\n ...\n}\n')),(0,o.kt)("p",null,"That's it! ",(0,o.kt)("strong",{parentName:"p"},"LiveViewJS")," will automatically handle the rest. The user will be able to drag and drop files into the div\nand they will be added to the entries of that upload config. \ud83e\udd2f"),(0,o.kt)("h2",{id:"credit-where-credit-is-due"},"Credit where credit is due"),(0,o.kt)("p",null,"Thanks to the Phoenix LiveView folks that built this! \ud83d\ude4c This is a great example of why we built on top of the existing\nLiveView client-side JS."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/bac9275f.48905d42.js b/docs/assets/js/bac9275f.48905d42.js deleted file mode 100644 index d392e89e..00000000 --- a/docs/assets/js/bac9275f.48905d42.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[4161],{4469:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-blog","id":"default"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/bc63dc68.3f7e97f5.js b/docs/assets/js/bc63dc68.3f7e97f5.js deleted file mode 100644 index 094dc1fb..00000000 --- a/docs/assets/js/bc63dc68.3f7e97f5.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9119],{876:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>u});var a=n(2784);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?s(Object(n),!0).forEach((function(t){o(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):s(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,o=function(e,t){if(null==e)return{};var n,a,o={},s=Object.keys(e);for(a=0;a<s.length;a++)n=s[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(a=0;a<s.length;a++)n=s[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var i=a.createContext({}),c=function(e){var t=a.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},m=function(e){var t=c(e.components);return a.createElement(i.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},p=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,s=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),p=c(n),u=o,v=p["".concat(i,".").concat(u)]||p[u]||d[u]||s;return n?a.createElement(v,r(r({ref:t},m),{},{components:n})):a.createElement(v,r({ref:t},m))}));function u(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var s=n.length,r=new Array(s);r[0]=p;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l.mdxType="string"==typeof e?e:o,r[1]=l;for(var c=2;c<s;c++)r[c]=n[c];return a.createElement.apply(null,r)}return a.createElement.apply(null,n)}p.displayName="MDXCreateElement"},1905:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>r,default:()=>d,frontMatter:()=>s,metadata:()=>l,toc:()=>c});var a=n(7896),o=(n(2784),n(876));const s={sidebar_position:2},r="Add/Remove Class Commands",l={unversionedId:"js-commands/add-remove-class",id:"js-commands/add-remove-class",title:"Add/Remove Class Commands",description:"Add or remove css classes including optional transition classes from an element using the addclass and removeclass",source:"@site/docs/11-js-commands/add-remove-class.md",sourceDirName:"11-js-commands",slug:"/js-commands/add-remove-class",permalink:"/docs/js-commands/add-remove-class",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Overview",permalink:"/docs/js-commands/overview"},next:{title:"Set/Remove Attribute Commands",permalink:"/docs/js-commands/set-remove-attr"}},i={},c=[{value:"Add Class Command",id:"add-class-command",level:2},{value:"Remove Class Command",id:"remove-class-command",level:2}],m={toc:c};function d(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"addremove-class-commands"},"Add/Remove Class Commands"),(0,o.kt)("p",null,"Add or remove css classes including optional transition classes from an element using the ",(0,o.kt)("inlineCode",{parentName:"p"},"add_class")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"remove_class"),"\ncommands."),(0,o.kt)("h2",{id:"add-class-command"},"Add Class Command"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"add_class")," command adds one or more css classes to an element."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().add_class(names: string, options?: ClassOptions)\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"names")," - A string of space separated css class names to add to the element"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element to add the class to. Defaults to the element that the JS Command is\nattached to."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"time")," - The time (in milliseconds) over which to apply the transition options. Defaults to 200"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"transition")," - The string of classes to apply before adding the classes, or a 3-tuple containing the transition\nclass, the class to apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out\nduration-300", "opacity-0", "opacity-100"]')))),(0,o.kt)("p",null,"Examples"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// add the "bg-blue-500" class to this button element on click\n<button phx-click="${new JS().add_class("bg-blue-500")}">Add Class</button>\n\n// add the "bg-blue-500" class to the element with id="target" on click\n<button phx-click="${new JS().add_class("bg-blue-500", {to: "#target"})}">Add Class</button>\n<div id="target">My Target</div>\n\n// add the "bg-blue-500" class to the element with id="target2" on click with a transition over 400ms\n<button phx-click="${new JS().add_class("bg-blue-500", {to: "#target2", transition: ["ease-out duration-300", "opacity-0", "opacity-100"], time: 400})}">Add Class</button>\n<div id="target2">My Target2</div>\n')),(0,o.kt)("h2",{id:"remove-class-command"},"Remove Class Command"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"remove_class")," command removes one or more css classes from an element."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().remove_class(names: string, options?: ClassOptions)\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"names")," - A string of space separated css class names to remove from the element"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element to remove the class from. Defaults to the element that the JS Command\nis attached to."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"time")," - The time (in milliseconds) over which to apply the transition options. Defaults to 200"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"transition")," - The string of classes to apply before removing the classes, or a 3-tuple containing the transition\nclass, the class to apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out\nduration-300", "opacity-0", "opacity-100"]')))),(0,o.kt)("p",null,"Examples"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// remove the "bg-blue-500" class from this button element on click\n<button class="bg-blue-500" phx-click="${new JS().remove_class("bg-blue-500")}">Remove Class</button>\n\n// remove the "bg-blue-500" class from the element with id="target" on click\n<button phx-click="${new JS().remove_class("bg-blue-500", {to: "#target"})}">Remove Class</button>\n<div id="target" class="bg-blue-500">My Target</div>\n\n// remove the "bg-blue-500" class from the element with id="target2" on click with a transition over 400ms\n<button phx-click="${new JS().remove_class("bg-blue-500", {to: "#target2", transition: ["ease-out duration-300", "opacity-0", "opacity-100"], time: 400})}">Remove Class</button>\n<div id="target2" class="bg-blue-500">My Target2</div>\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/be227171.2d38958c.js b/docs/assets/js/be227171.2d38958c.js deleted file mode 100644 index ebf72c03..00000000 --- a/docs/assets/js/be227171.2d38958c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6300],{876:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>c});var a=t(2784);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?r(Object(t),!0).forEach((function(n){i(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):r(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,a,i=function(e,n){if(null==e)return{};var t,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)t=r[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),d=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},s=function(e){var n=d(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},h=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),h=d(t),c=i,u=h["".concat(p,".").concat(c)]||h[c]||m[c]||r;return t?a.createElement(u,o(o({ref:n},s),{},{components:t})):a.createElement(u,o({ref:n},s))}));function c(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=h;var l={};for(var p in n)hasOwnProperty.call(n,p)&&(l[p]=n[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var d=2;d<r;d++)o[d]=t[d];return a.createElement.apply(null,o)}return a.createElement.apply(null,t)}h.displayName="MDXCreateElement"},296:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>l,toc:()=>d});var a=t(7896),i=(t(2784),t(876));const r={sidebar_position:5},o="LiveView API - `handleParams`",l={unversionedId:"anatomy-of-a-liveview/handle-params",id:"anatomy-of-a-liveview/handle-params",title:"LiveView API - `handleParams`",description:"Let's explore the handleParams method. Since the previous example (counterLiveView) did not use handleParams,",source:"@site/docs/03-anatomy-of-a-liveview/handle-params.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/handle-params",permalink:"/docs/anatomy-of-a-liveview/handle-params",draft:!1,tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"LiveView API - `handleEvent`",permalink:"/docs/anatomy-of-a-liveview/handle-event-details"},next:{title:"LiveView API - `handleInfo`",permalink:"/docs/anatomy-of-a-liveview/handle-info"}},p={},d=[{value:"Example Renders",id:"example-renders",level:2},{value:"<code>handleParams</code> Method",id:"handleparams-method",level:2},{value:"<code>handleParams</code> Signature",id:"handleparams-signature",level:2}],s={toc:d};function m(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveview-api---handleparams"},"LiveView API - ",(0,i.kt)("inlineCode",{parentName:"h1"},"handleParams")),(0,i.kt)("p",null,"Let's explore the ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," method. Since the previous example (",(0,i.kt)("inlineCode",{parentName:"p"},"counterLiveView"),") did not use ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams"),",\nwe'll define ",(0,i.kt)("inlineCode",{parentName:"p"},"helloLiveView.ts")," and explore the ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," method with it. As you can see below, ",(0,i.kt)("inlineCode",{parentName:"p"},"helloLiveView.ts"),"\ndefines ",(0,i.kt)("inlineCode",{parentName:"p"},"mount"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"render"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="helloLiveView.ts"',title:'"helloLiveView.ts"'},'export const helloLiveView = createLiveView({\n mount: (socket) => {\n socket.assign({ name: "World" });\n },\n handleParams(url, socket) {\n const name = url.searchParams.get("name") || "World";\n socket.assign({ name });\n },\n render: (context) => html`Hello ${context.name}!`,\n});\n')),(0,i.kt)("p",null,"In the case of ",(0,i.kt)("inlineCode",{parentName:"p"},"helloLiveView"),", we are using ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," to update the ",(0,i.kt)("inlineCode",{parentName:"p"},"name")," in the ",(0,i.kt)("inlineCode",{parentName:"p"},"context")," based on the ",(0,i.kt)("inlineCode",{parentName:"p"},"name"),"\nquery parameter in the ",(0,i.kt)("inlineCode",{parentName:"p"},"URL"),". If there is no ",(0,i.kt)("inlineCode",{parentName:"p"},"name")," query parameter, we default to ",(0,i.kt)("inlineCode",{parentName:"p"},"World"),"."),(0,i.kt)("h2",{id:"example-renders"},"Example Renders"),(0,i.kt)("p",null,"Let's say you have the ",(0,i.kt)("inlineCode",{parentName:"p"},"helloLiveView")," routed to ",(0,i.kt)("inlineCode",{parentName:"p"},"/hello"),". Visiting the following paths would result in the following\nrenders:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"/hello")," - ",(0,i.kt)("inlineCode",{parentName:"li"},"Hello World!")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"/hello?name=LiveViewJS")," - ",(0,i.kt)("inlineCode",{parentName:"li"},"Hello LiveViewJS!")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"/hello?name=LiveViewJS&foo=bar")," - ",(0,i.kt)("inlineCode",{parentName:"li"},"Hello LiveViewJS!")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"/hello?name=LiveViewJS#blah")," - ",(0,i.kt)("inlineCode",{parentName:"li"},"Hello LiveViewJS!"))),(0,i.kt)("h2",{id:"handleparams-method"},(0,i.kt)("inlineCode",{parentName:"h2"},"handleParams")," Method"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," is automatically called by ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," on the initial load of a LiveView, as well as anytime the URL of\nthe LiveView changes. ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," allows developers to access the full ",(0,i.kt)("inlineCode",{parentName:"p"},"URL")," of the LiveView including the ",(0,i.kt)("inlineCode",{parentName:"p"},"host"),",\n",(0,i.kt)("inlineCode",{parentName:"p"},"path"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"hash"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"pathname"),", etc, and then update the ",(0,i.kt)("inlineCode",{parentName:"p"},"context")," of the ",(0,i.kt)("inlineCode",{parentName:"p"},"socket")," or otherwise respond to data in the ",(0,i.kt)("inlineCode",{parentName:"p"},"URL"),"."),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Worth noting that the http server (e.g., express or oak) handles the routing of the browser to this LiveView. This\nmeans that changes in the ",(0,i.kt)("inlineCode",{parentName:"p"},"URL")," for ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," are typically search parameters or hash changes. Changing the host\nand/or path of a URL will typically mean the server routes you to a different LiveView (if one exists at that host and\npath).")),(0,i.kt)("h2",{id:"handleparams-signature"},(0,i.kt)("inlineCode",{parentName:"h2"},"handleParams")," Signature"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"handleParams(url: URL, socket: LiveViewSocket<TContext, TInfos>): void | Promise<void>;\n")),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"URL")," passed to the ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," method is the standard ",(0,i.kt)("inlineCode",{parentName:"p"},"URL")," object, not a ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," specific ",(0,i.kt)("inlineCode",{parentName:"p"},"URL"),"\nobject. See the ",(0,i.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/API/URL"},"MDN URL documentation")," for more information.")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/c1a16298.5abdd7fc.js b/docs/assets/js/c1a16298.5abdd7fc.js deleted file mode 100644 index be481402..00000000 --- a/docs/assets/js/c1a16298.5abdd7fc.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6119],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?i(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),u=c(n),m=a,v=u["".concat(l,".").concat(m)]||u[m]||d[m]||i;return n?r.createElement(v,o(o({ref:t},p),{},{components:n})):r.createElement(v,o({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,o[1]=s;for(var c=2;c<i;c++)o[c]=n[c];return r.createElement.apply(null,o)}return r.createElement.apply(null,n)}u.displayName="MDXCreateElement"},1777:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>c});var r=n(7896),a=(n(2784),n(876));const i={sidebar_position:1},o="Overview",s={unversionedId:"forms-and-changesets/overview",id:"forms-and-changesets/overview",title:"Overview",description:"Forms are obviously extremely important for any web application that needs user input. Building, validating, and",source:"@site/docs/07-forms-and-changesets/overview.md",sourceDirName:"07-forms-and-changesets",slug:"/forms-and-changesets/overview",permalink:"/docs/forms-and-changesets/overview",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Forms & Changesets",permalink:"/docs/category/forms--changesets"},next:{title:"Changesets",permalink:"/docs/forms-and-changesets/changesets"}},l={},c=[{value:"Form Bindigs",id:"form-bindigs",level:2},{value:"Changesets",id:"changesets",level:2}],p={toc:c};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"overview"},"Overview"),(0,a.kt)("p",null,"Forms are obviously extremely important for any web application that needs user input. Building, validating, and\nhandling form submission is built into ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," forms ."),(0,a.kt)("h2",{id:"form-bindigs"},"Form Bindigs"),(0,a.kt)("p",null,"We've already reviewed the form event bindings that are available in LiveViewJS. Here is a quick summary:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-change")," - This event is sent to the server along with all the form values when any form input is changed."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"phx-submit")," - This event is sent to the server when the form is submitted alog with all the form values.")),(0,a.kt)("p",null,"Feel free to review form events in more detail in the\n",(0,a.kt)("a",{parentName:"p",href:"/docs/user-events-slash-bindings/overview"},"User Events and Bindings")," section."),(0,a.kt)("h2",{id:"changesets"},"Changesets"),(0,a.kt)("p",null,'We have not yet discussed the concept of a "changeset" in LiveViewJS. At a high level a changeset is a way to parse and\nvalidate that incoming JSON data maps to the expected constraints. You will see it is a very powerful concept that\nallows you to build and validate complex forms with ease.'),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"Changesets are a concept that is taken from an Elixir library called\n",(0,a.kt)("a",{parentName:"p",href:"https://hexdocs.pm/ecto/Ecto.Changeset.html"},"Ecto"),". Ecto changesets are used to validate and persist data to a\ndatabase. While ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," changeset are not ORM or DB releated, we've taken the concept of a changeset and adapted\nit to the Typescript world for parsing and validation.")),(0,a.kt)("p",null,"We will take a deep dive into Changesets in a more detail in the next section."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/c68b9697.0d9df512.js b/docs/assets/js/c68b9697.0d9df512.js deleted file mode 100644 index e5759cac..00000000 --- a/docs/assets/js/c68b9697.0d9df512.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[914],{6962:e=>{e.exports=JSON.parse('{"title":"LiveViewSocket","description":"The swiss army knife of LiveViewJS that connects the dots across a LiveView lifecycle.","slug":"/category/liveviewsocket","permalink":"/docs/category/liveviewsocket","navigation":{"previous":{"title":"Pub/Sub with `handleInfo`","permalink":"/docs/anatomy-of-a-liveview/handle-info-pub-sub"},"next":{"title":"LiveViewSocket API","permalink":"/docs/liveview-socket/liveviewsocket-api"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/ccc49370.016b92cf.js b/docs/assets/js/ccc49370.016b92cf.js deleted file mode 100644 index fb9a65c7..00000000 --- a/docs/assets/js/ccc49370.016b92cf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6103],{1651:(e,t,n)=>{n.r(t),n.d(t,{default:()=>h});var a=n(2784),l=n(6277),r=n(328),o=n(211),i=n(1375),c=n(3485),s=n(4646),m=n(7896),u=n(1077),d=n(7066);function v(e){const{nextItem:t,prevItem:n}=e;return a.createElement("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,u.I)({id:"theme.blog.post.paginator.navAriaLabel",message:"Blog post page navigation",description:"The ARIA label for the blog posts pagination"})},n&&a.createElement(d.Z,(0,m.Z)({},n,{subLabel:a.createElement(u.Z,{id:"theme.blog.post.paginator.newerPost",description:"The blog post button label to navigate to the newer/previous post"},"Newer Post")})),t&&a.createElement(d.Z,(0,m.Z)({},t,{subLabel:a.createElement(u.Z,{id:"theme.blog.post.paginator.olderPost",description:"The blog post button label to navigate to the older/next post"},"Older Post"),isNext:!0})))}function g(){var e;const{assets:t,metadata:n}=(0,i.C)(),{title:l,description:o,date:c,tags:s,authors:m,frontMatter:u}=n,{keywords:d}=u,v=null!=(e=t.image)?e:u.image;return a.createElement(r.d,{title:l,description:o,keywords:d,image:v},a.createElement("meta",{property:"og:type",content:"article"}),a.createElement("meta",{property:"article:published_time",content:c}),m.some((e=>e.url))&&a.createElement("meta",{property:"article:author",content:m.map((e=>e.url)).filter(Boolean).join(",")}),s.length>0&&a.createElement("meta",{property:"article:tag",content:s.map((e=>e.label)).join(",")}))}var f=n(8188);function p(e){let{sidebar:t,children:n}=e;const{metadata:l,toc:r}=(0,i.C)(),{nextItem:o,prevItem:m,frontMatter:u}=l,{hide_table_of_contents:d,toc_min_heading_level:g,toc_max_heading_level:p}=u;return a.createElement(c.Z,{sidebar:t,toc:!d&&r.length>0?a.createElement(f.Z,{toc:r,minHeadingLevel:g,maxHeadingLevel:p}):void 0},a.createElement(s.Z,null,n),(o||m)&&a.createElement(v,{nextItem:o,prevItem:m}))}function h(e){const t=e.content;return a.createElement(i.n,{content:e.content,isBlogPostPage:!0},a.createElement(r.FG,{className:(0,l.Z)(o.k.wrapper.blogPages,o.k.page.blogPostPage)},a.createElement(g,null),a.createElement(p,{sidebar:e.sidebar},a.createElement(t,null))))}},8188:(e,t,n)=>{n.d(t,{Z:()=>c});var a=n(7896),l=n(2784),r=n(6277),o=n(6986);const i="tableOfContents_TN1Q";function c(e){let{className:t,...n}=e;return l.createElement("div",{className:(0,r.Z)(i,"thin-scrollbar",t)},l.createElement(o.Z,(0,a.Z)({},n,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}},6986:(e,t,n)=>{n.d(t,{Z:()=>g});var a=n(7896),l=n(2784),r=n(7683);function o(e){const t=e.map((e=>({...e,parentIndex:-1,children:[]}))),n=Array(7).fill(-1);t.forEach(((e,t)=>{const a=n.slice(2,e.level);e.parentIndex=Math.max(...a),n[e.level]=t}));const a=[];return t.forEach((e=>{const{parentIndex:n,...l}=e;n>=0?t[n].children.push(l):a.push(l)})),a}function i(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return t.flatMap((e=>{const t=i({toc:e.children,minHeadingLevel:n,maxHeadingLevel:a});return function(e){return e.level>=n&&e.level<=a}(e)?[{...e,children:t}]:t}))}function c(e){const t=e.getBoundingClientRect();return t.top===t.bottom?c(e.parentNode):t}function s(e,t){var n;let{anchorTopOffset:a}=t;const l=e.find((e=>c(e).top>=a));if(l){var r;return function(e){return e.top>0&&e.bottom<window.innerHeight/2}(c(l))?l:null!=(r=e[e.indexOf(l)-1])?r:null}return null!=(n=e[e.length-1])?n:null}function m(){const e=(0,l.useRef)(0),{navbar:{hideOnScroll:t}}=(0,r.L)();return(0,l.useEffect)((()=>{e.current=t?0:document.querySelector(".navbar").clientHeight}),[t]),e}function u(e){const t=(0,l.useRef)(void 0),n=m();(0,l.useEffect)((()=>{if(!e)return()=>{};const{linkClassName:a,linkActiveClassName:l,minHeadingLevel:r,maxHeadingLevel:o}=e;function i(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(a),i=function(e){let{minHeadingLevel:t,maxHeadingLevel:n}=e;const a=[];for(let l=t;l<=n;l+=1)a.push("h"+l+".anchor");return Array.from(document.querySelectorAll(a.join()))}({minHeadingLevel:r,maxHeadingLevel:o}),c=s(i,{anchorTopOffset:n.current}),m=e.find((e=>c&&c.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e)));e.forEach((e=>{!function(e,n){n?(t.current&&t.current!==e&&t.current.classList.remove(l),e.classList.add(l),t.current=e):e.classList.remove(l)}(e,e===m)}))}return document.addEventListener("scroll",i),document.addEventListener("resize",i),i(),()=>{document.removeEventListener("scroll",i),document.removeEventListener("resize",i)}}),[e,n])}function d(e){let{toc:t,className:n,linkClassName:a,isChild:r}=e;return t.length?l.createElement("ul",{className:r?void 0:n},t.map((e=>l.createElement("li",{key:e.id},l.createElement("a",{href:"#"+e.id,className:null!=a?a:void 0,dangerouslySetInnerHTML:{__html:e.value}}),l.createElement(d,{isChild:!0,toc:e.children,className:n,linkClassName:a}))))):null}const v=l.memo(d);function g(e){let{toc:t,className:n="table-of-contents table-of-contents__left-border",linkClassName:c="table-of-contents__link",linkActiveClassName:s,minHeadingLevel:m,maxHeadingLevel:d,...g}=e;const f=(0,r.L)(),p=null!=m?m:f.tableOfContents.minHeadingLevel,h=null!=d?d:f.tableOfContents.maxHeadingLevel,b=function(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return(0,l.useMemo)((()=>i({toc:o(t),minHeadingLevel:n,maxHeadingLevel:a})),[t,n,a])}({toc:t,minHeadingLevel:p,maxHeadingLevel:h});return u((0,l.useMemo)((()=>{if(c&&s)return{linkClassName:c,linkActiveClassName:s,minHeadingLevel:p,maxHeadingLevel:h}}),[c,s,p,h])),l.createElement(v,(0,a.Z)({toc:b,className:n,linkClassName:c},g))}}}]); \ No newline at end of file diff --git a/docs/assets/js/ccf3ea07.ed328d0a.js b/docs/assets/js/ccf3ea07.ed328d0a.js deleted file mode 100644 index 96d71183..00000000 --- a/docs/assets/js/ccf3ea07.ed328d0a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[7875],{876:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var i=t(2784);function r(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function o(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?a(Object(t),!0).forEach((function(n){r(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):a(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,i,r=function(e,n){if(null==e)return{};var t,i,r={},a=Object.keys(e);for(i=0;i<a.length;i++)t=a[i],n.indexOf(t)>=0||(r[t]=e[t]);return r}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i<a.length;i++)t=a[i],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var s=i.createContext({}),d=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=d(e.components);return i.createElement(s.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},c=i.forwardRef((function(e,n){var t=e.components,r=e.mdxType,a=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=d(t),m=r,g=c["".concat(s,".").concat(m)]||c[m]||p[m]||a;return t?i.createElement(g,o(o({ref:n},u),{},{components:t})):i.createElement(g,o({ref:n},u))}));function m(e,n){var t=arguments,r=n&&n.mdxType;if("string"==typeof e||r){var a=t.length,o=new Array(a);o[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var d=2;d<a;d++)o[d]=t[d];return i.createElement.apply(null,o)}return i.createElement.apply(null,t)}c.displayName="MDXCreateElement"},7517:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>l,toc:()=>d});var i=t(7896),r=(t(2784),t(876));const a={sidebar_position:2},o="Additional Bindings",l={unversionedId:"user-events-slash-bindings/additional-bindings",id:"user-events-slash-bindings/additional-bindings",title:"Additional Bindings",description:"There are additional bindings outside of the four main bindings for User Events that you will find extremely useful and",source:"@site/docs/06-user-events-slash-bindings/additional-bindings.md",sourceDirName:"06-user-events-slash-bindings",slug:"/user-events-slash-bindings/additional-bindings",permalink:"/docs/user-events-slash-bindings/additional-bindings",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"User Events / Bindings",permalink:"/docs/user-events-slash-bindings/overview"},next:{title:"Rate Limiting Bindings",permalink:"/docs/user-events-slash-bindings/rate-limiting-bindings"}},s={},d=[{value:"Value Bindings",id:"value-bindings",level:2},{value:"Value Binding Example",id:"value-binding-example",level:3}],u={toc:d};function p(e){let{components:n,...t}=e;return(0,r.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"additional-bindings"},"Additional Bindings"),(0,r.kt)("p",null,"There are additional bindings outside of the four main bindings for User Events that you will find extremely useful and\nthat you will use often."),(0,r.kt)("h2",{id:"value-bindings"},"Value Bindings"),(0,r.kt)("p",null,'When you need to send along additional data with an event binding you can use a "value binding" which looks something\nlike ',(0,r.kt)("inlineCode",{parentName:"p"},"phx-value-[NAME]")," where ",(0,r.kt)("inlineCode",{parentName:"p"},"[NAME]")," is replaced by the key of the value you want to pass. This binding can be used in\nconjunction with other click, key, and focus bindings."),(0,r.kt)("h3",{id:"value-binding-example"},"Value Binding Example"),(0,r.kt)("p",null,"For example let's say you want to send the ",(0,r.kt)("inlineCode",{parentName:"p"},"mark_complete")," event to the server along with and ",(0,r.kt)("inlineCode",{parentName:"p"},"id")," value\n(e.g., ",(0,r.kt)("inlineCode",{parentName:"p"},'{id: "myId"}'),') when the user clicks on the "Complete" button. To do this you do the following:'),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-html"},'<button phx-click="mark_complete" phx-value-id="myId">Complete</button>\n')),(0,r.kt)("p",null,"Note the ",(0,r.kt)("inlineCode",{parentName:"p"},"[NAME]")," part of ",(0,r.kt)("inlineCode",{parentName:"p"},"phx-value-[NAME]")," is ",(0,r.kt)("inlineCode",{parentName:"p"},"id")," used as the object key while the attribute value (i.e., ",(0,r.kt)("inlineCode",{parentName:"p"},'"myId"'),") is\nused as the object value."),(0,r.kt)("p",null,"This example would send the following event to the server:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'{\n type: "mark_complete",\n id: "myId"\n}\n')))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/d4048a91.0f420394.js b/docs/assets/js/d4048a91.0f420394.js deleted file mode 100644 index 5feaf0c4..00000000 --- a/docs/assets/js/d4048a91.0f420394.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1523],{876:(t,e,n)=>{n.d(e,{Zo:()=>m,kt:()=>u});var a=n(2784);function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function l(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function i(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?l(Object(n),!0).forEach((function(e){r(t,e,n[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):l(Object(n)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))}))}return t}function d(t,e){if(null==t)return{};var n,a,r=function(t,e){if(null==t)return{};var n,a,r={},l=Object.keys(t);for(a=0;a<l.length;a++)n=l[a],e.indexOf(n)>=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(t);for(a=0;a<l.length;a++)n=l[a],e.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var p=a.createContext({}),o=function(t){var e=a.useContext(p),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},m=function(t){var e=o(t.components);return a.createElement(p.Provider,{value:e},t.children)},k={inlineCode:"code",wrapper:function(t){var e=t.children;return a.createElement(a.Fragment,{},e)}},s=a.forwardRef((function(t,e){var n=t.components,r=t.mdxType,l=t.originalType,p=t.parentName,m=d(t,["components","mdxType","originalType","parentName"]),s=o(n),u=r,g=s["".concat(p,".").concat(u)]||s[u]||k[u]||l;return n?a.createElement(g,i(i({ref:e},m),{},{components:n})):a.createElement(g,i({ref:e},m))}));function u(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var l=n.length,i=new Array(l);i[0]=s;var d={};for(var p in e)hasOwnProperty.call(e,p)&&(d[p]=e[p]);d.originalType=t,d.mdxType="string"==typeof t?t:r,i[1]=d;for(var o=2;o<l;o++)i[o]=n[o];return a.createElement.apply(null,i)}return a.createElement.apply(null,n)}s.displayName="MDXCreateElement"},4317:(t,e,n)=>{n.r(e),n.d(e,{assets:()=>p,contentTitle:()=>i,default:()=>k,frontMatter:()=>l,metadata:()=>d,toc:()=>o});var a=n(7896),r=(n(2784),n(876));const l={sidebar_position:4},i="Phoenix LiveView Bindings",d={unversionedId:"user-events-slash-bindings/bindings-table",id:"user-events-slash-bindings/bindings-table",title:"Phoenix LiveView Bindings",description:"Here is a table of all the bindings available in Phoenix LiveView and whether they are available in LiveViewJS.",source:"@site/docs/06-user-events-slash-bindings/bindings-table.md",sourceDirName:"06-user-events-slash-bindings",slug:"/user-events-slash-bindings/bindings-table",permalink:"/docs/user-events-slash-bindings/bindings-table",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Rate Limiting Bindings",permalink:"/docs/user-events-slash-bindings/rate-limiting-bindings"},next:{title:"Forms & Changesets",permalink:"/docs/category/forms--changesets"}},p={},o=[],m={toc:o};function k(t){let{components:e,...n}=t;return(0,r.kt)("wrapper",(0,a.Z)({},m,n,{components:e,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"phoenix-liveview-bindings"},"Phoenix LiveView Bindings"),(0,r.kt)("p",null,"Here is a table of all the bindings available in Phoenix LiveView and whether they are available in LiveViewJS."),(0,r.kt)("admonition",{type:"info"},(0,r.kt)("p",{parentName:"admonition"},"These bindings actually come from ",(0,r.kt)("a",{parentName:"p",href:"https://hexdocs.pm/phoenix_live_view/bindings.html"},"Phoenix LiveView"),' since\nwe use the same client-side JavaScript library. The table below denotes which bindings are "Supported" in ',(0,r.kt)("strong",{parentName:"p"},"LiveViewJS"),"\nand which are not. Bindings below marked with \u2705 are working and tested and most of them have example usage in the\n",(0,r.kt)("inlineCode",{parentName:"p"},"examples")," codebase. Those with ",(0,r.kt)("inlineCode",{parentName:"p"},"?"),", we have not gotten around to testing so not sure if they work. Those marked with \u274c\nare not yet implemented and known not to work.")),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"Binding"),(0,r.kt)("th",{parentName:"tr",align:null},"Attribute"),(0,r.kt)("th",{parentName:"tr",align:null},"Supported"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Params"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-value-*")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Click Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-click")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Click Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-click-away")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Form Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-change")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Form Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-submit")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Form Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-feedback-for")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Form Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-disable-with")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Form Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-trigger-action")),(0,r.kt)("td",{parentName:"tr",align:null},"\ufe56")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Form Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-auto-recover")),(0,r.kt)("td",{parentName:"tr",align:null},"\ufe56")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Focus Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-blur")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Focus Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-focus")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Focus Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-window-blur")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Focus Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-window-focus")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Key Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-keydown")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Key Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-keyup")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Key Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-window-keydown")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Key Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-window-keyup")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Key Events"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-key")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"DOM Patching"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-update")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"DOM Patching"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-remove")),(0,r.kt)("td",{parentName:"tr",align:null},"\ufe56")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"JS Interop"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-hook")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Rate Limiting"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-debounce")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Rate Limiting"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-throttle")),(0,r.kt)("td",{parentName:"tr",align:null},"\u2705")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},"Static Tracking"),(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"phx-track-static")),(0,r.kt)("td",{parentName:"tr",align:null},"\u274c")))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/d5bbcc50.33ddf498.js b/docs/assets/js/d5bbcc50.33ddf498.js deleted file mode 100644 index a19485ca..00000000 --- a/docs/assets/js/d5bbcc50.33ddf498.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9843],{876:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>b});var r=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function u(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},s=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,s=u(e,["components","mdxType","originalType","parentName"]),m=l(n),b=i,d=m["".concat(c,".").concat(b)]||m[b]||p[b]||o;return n?r.createElement(d,a(a({ref:t},s),{},{components:n})):r.createElement(d,a({ref:t},s))}));function b(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=m;var u={};for(var c in t)hasOwnProperty.call(t,c)&&(u[c]=t[c]);u.originalType=e,u.mdxType="string"==typeof e?e:i,a[1]=u;for(var l=2;l<o;l++)a[l]=n[l];return r.createElement.apply(null,a)}return r.createElement.apply(null,n)}m.displayName="MDXCreateElement"},2625:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>o,metadata:()=>u,toc:()=>l});var r=n(7896),i=(n(2784),n(876));const o={sidebar_position:2},a="Example Pub/Sub LiveView",u={unversionedId:"real-time-multi-player-pub-sub/example-pub-sub",id:"real-time-multi-player-pub-sub/example-pub-sub",title:"Example Pub/Sub LiveView",description:"We're going to extend our counter example from learning the LiveView API to",source:"@site/docs/09-real-time-multi-player-pub-sub/example-pub-sub.md",sourceDirName:"09-real-time-multi-player-pub-sub",slug:"/real-time-multi-player-pub-sub/example-pub-sub",permalink:"/docs/real-time-multi-player-pub-sub/example-pub-sub",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Overview",permalink:"/docs/real-time-multi-player-pub-sub/overview"},next:{title:"Client-side Javascript",permalink:"/docs/category/client-side-javascript"}},c={},l=[{value:"How it works",id:"how-it-works",level:2},{value:"It's that easy!",id:"its-that-easy",level:2}],s={toc:l};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"example-pubsub-liveview"},"Example Pub/Sub LiveView"),(0,i.kt)("p",null,"We're going to extend our counter example from ",(0,i.kt)("a",{parentName:"p",href:"/docs/anatomy-of-a-liveview/liveview-api"},"learning the LiveView API")," to\nuse Pub/Sub which will make it a real-time, multi-player counter. Here is the code with the Pub/Sub changes highlighted:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="realtimeCounterLiveView.ts" {3-6,14,17-20,27-28,31-32,36-40}',title:'"realtimeCounterLiveView.ts"',"{3-6,14,17-20,27-28,31-32,36-40}":!0},'import { createLiveView, html, SingleProcessPubSub } from "liveviewjs";\n\n// An in-memory count simulating state outside of the LiveView\nlet count = 0;\n// Use a single process pub/sub implementation (for simplicity)\nconst pubSub = new SingleProcessPubSub();\n\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const rtCounterLiveView = createLiveView<\n { count: number }, // Define LiveView Context / State\n { type: "increment" } | { type: "decrement" }, // Define LiveView Events\n { type: "counter"; count: number } // Define LiveView Info messages\n>({\n mount: (socket) => {\n // init state, set count to current count\n socket.assign({ count });\n // subscribe to counter events\n socket.subscribe("counter");\n },\n handleEvent: (event, socket) => {\n // handle increment and decrement events\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n // broadcast the new count\n pubSub.broadcast("counter", { count: count + 1 });\n break;\n case "decrement":\n // broadcast the new count\n pubSub.broadcast("counter", { count: count - 1 });\n break;\n }\n },\n handleInfo: (info, socket) => {\n // receive updates from pubsub and update the context\n count = info.count;\n socket.assign({ count });\n },\n render: async (context) => {\n // render the view based on the state\n const { count } = context;\n return html`\n <div>\n <h1>Count is: ${count}</h1>\n <button phx-click="decrement">-</button>\n <button phx-click="increment">+</button>\n </div>\n `;\n },\n});\n')),(0,i.kt)("h2",{id:"how-it-works"},"How it works"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"This works just like the ",(0,i.kt)("inlineCode",{parentName:"li"},"counter.ts")," example except we're using Pub/Sub to broadcast the new count to all connected\nclients and subscribe to updates from other clients."),(0,i.kt)("li",{parentName:"ul"},"When a client clicks the increment or decrement button, we broadcast the new count to all connected clients using\n",(0,i.kt)("inlineCode",{parentName:"li"},"pubSub.broadcast"),"."),(0,i.kt)("li",{parentName:"ul"},"The ",(0,i.kt)("strong",{parentName:"li"},"LiveViewJS")," framework automatically routes messages from ",(0,i.kt)("inlineCode",{parentName:"li"},"pubSub.broadcast")," to the ",(0,i.kt)("inlineCode",{parentName:"li"},"handleInfo")," function for\nany LiveView subscribed to the topic."),(0,i.kt)("li",{parentName:"ul"},"In this case, ",(0,i.kt)("inlineCode",{parentName:"li"},"handleInfo")," receives the new count and updates the LiveView context which re-renders the view.")),(0,i.kt)("h2",{id:"its-that-easy"},"It's that easy!"),(0,i.kt)("p",null,"In ~10 lines of code we've built a real-time, multi-player counter! Sure a real-time counter isn't particularly useful\nbut shows you how easy it is to create real-time, multi-player applications with very little code and very little\neffort."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/d63d7ef8.33b1b0a0.js b/docs/assets/js/d63d7ef8.33b1b0a0.js deleted file mode 100644 index 8e81a4f1..00000000 --- a/docs/assets/js/d63d7ef8.33b1b0a0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8685],{876:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>b});var n=r(2784);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){o(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},v=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,p=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),v=l(r),b=o,d=v["".concat(p,".").concat(b)]||v[b]||c[b]||i;return r?n.createElement(d,a(a({ref:t},u),{},{components:r})):n.createElement(d,a({ref:t},u))}));function b(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=v;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var l=2;l<i;l++)a[l]=r[l];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}v.displayName="MDXCreateElement"},14:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>c,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var n=r(7896),o=(r(2784),r(876));const i={sidebar_position:3},a='Support Webserver "X"',s={unversionedId:"webserver-integration/support-webserver-x",id:"webserver-integration/support-webserver-x",title:'Support Webserver "X"',description:"If you want to use LiveViewJS with a webserver that is not supported out of the box, you can implement the",source:"@site/docs/12-webserver-integration/support-webserver-x.md",sourceDirName:"12-webserver-integration",slug:"/webserver-integration/support-webserver-x",permalink:"/docs/webserver-integration/support-webserver-x",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"LiveViewServerAdaptor",permalink:"/docs/webserver-integration/liveview-server-adaptor"},next:{title:"Miscellaneous",permalink:"/docs/category/miscellaneous"}},p={},l=[{value:"Look at the existing integrations",id:"look-at-the-existing-integrations",level:2},{value:"Open an issue",id:"open-an-issue",level:2}],u={toc:l};function c(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"support-webserver-x"},'Support Webserver "X"'),(0,o.kt)("p",null,"If you want to use LiveViewJS with a webserver that is not supported out of the box, you can implement the\n",(0,o.kt)("inlineCode",{parentName:"p"},"LiveViewServerAdaptor")," interface and plug it into your webserver."),(0,o.kt)("p",null,"Essentially, you'll need to be able to intercept HTTP and websocket requests and pass them to the LiveViewJS library.\nThe LiveViewJS library will then handle the requests and return the appropriate responses."),(0,o.kt)("h2",{id:"look-at-the-existing-integrations"},"Look at the existing integrations"),(0,o.kt)("p",null,"Checkout the LiveViewJS source code and look at the\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/blob/main/packages/express/src/node/server.ts"},(0,o.kt)("inlineCode",{parentName:"a"},"NodeExpressLiveViewServer"))," and\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/blob/main/packages/deno/src/deno/server.ts"},(0,o.kt)("inlineCode",{parentName:"a"},"DenoOakLiveViewServer"))," classes.\nThese are the two webserver integrations that are supported out of the box."),(0,o.kt)("h2",{id:"open-an-issue"},"Open an issue"),(0,o.kt)("p",null,"We are happy to help you get LiveViewJS working with your webserver. If you\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs/issues"},"open an issue")," on the LiveViewJS GitHub repo, we'll be happy to support\nyou."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/d6e2e8d0.96c3ccce.js b/docs/assets/js/d6e2e8d0.96c3ccce.js deleted file mode 100644 index 059f4945..00000000 --- a/docs/assets/js/d6e2e8d0.96c3ccce.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6242],{876:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>c});var r=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var m=r.createContext({}),s=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(m.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,m=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=s(n),c=a,b=p["".concat(m,".").concat(c)]||p[c]||d[c]||o;return n?r.createElement(b,i(i({ref:t},u),{},{components:n})):r.createElement(b,i({ref:t},u))}));function c(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=p;var l={};for(var m in t)hasOwnProperty.call(t,m)&&(l[m]=t[m]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s<o;s++)i[s]=n[s];return r.createElement.apply(null,i)}return r.createElement.apply(null,n)}p.displayName="MDXCreateElement"},3779:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>m,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7896),a=(n(2784),n(876));const o={sidebar_position:3},i="Set/Remove Attribute Commands",l={unversionedId:"js-commands/set-remove-attr",id:"js-commands/set-remove-attr",title:"Set/Remove Attribute Commands",description:"Set or remove an attribute from an HTML element using the setattribute and removeattribute commands.",source:"@site/docs/11-js-commands/set-remove-attr.md",sourceDirName:"11-js-commands",slug:"/js-commands/set-remove-attr",permalink:"/docs/js-commands/set-remove-attr",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Add/Remove Class Commands",permalink:"/docs/js-commands/add-remove-class"},next:{title:"Show/Hide/Toggle Element Commands",permalink:"/docs/js-commands/show-hide-toggle-el"}},m={},s=[{value:"Set Attribute Command",id:"set-attribute-command",level:2},{value:"Remove Attribute Command",id:"remove-attribute-command",level:2}],u={toc:s};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"setremove-attribute-commands"},"Set/Remove Attribute Commands"),(0,a.kt)("p",null,"Set or remove an attribute from an HTML element using the ",(0,a.kt)("inlineCode",{parentName:"p"},"set_attribute")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"remove_attribute")," commands."),(0,a.kt)("h2",{id:"set-attribute-command"},"Set Attribute Command"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"set_attribute")," command add or updates a single attribute on the target element."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().set_attribute(attr: [string, string], options?: AttributeOptions)\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"attr")," - the 2-tuple of the attribute name and value to set"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element to set the attribute on. Defaults to the element that the JS Command\nis attached to.")))),(0,a.kt)("p",null,"Examples"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// set the "disabled" attribute to on the target button element on click\n<button phx-click="${new JS().set_attribute(["disabled", ""], { to: "#target" })}">Set Disabled</button>\n<button id="target">Target</button>\n\n// set the "aria-expanded" attribute to "true" on the target element on click\n<button phx-click={new JS().set_attribute(["aria-expanded", "true"], to: "#dropdown")}>\n Expand Dropdown\n</button>\n<div id="dropdown" aria-expanded="false">Dropdown</div>\n')),(0,a.kt)("h2",{id:"remove-attribute-command"},"Remove Attribute Command"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"remove_attribute")," command removes a single attribute from the target element."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().remove_attribute(attr: string, options?: AttributeOptions)\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"attr")," - the attribute name to remove"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element to remove the attribute from. Defaults to the element that the JS\nCommand is attached to.")))),(0,a.kt)("p",null,"Examples"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// remove the "disabled" attribute from the target button element on click\n<button phx-click="${new JS().remove_attribute("disabled", { to: "#target" })}">Remove Disabled</button>\n<button id="target" disabled>Target</button>\n\n// remove the "aria-expanded" attribute from the target element on click\n<button phx-click={new JS().remove_attribute("aria-expanded", to: "#dropdown")}>\n Close Dropdown\n</button>\n<div id="dropdown" aria-expanded="true">Dropdown</div>\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/d8b15fc0.f830a4a0.js b/docs/assets/js/d8b15fc0.f830a4a0.js deleted file mode 100644 index 2bd669e5..00000000 --- a/docs/assets/js/d8b15fc0.f830a4a0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1087],{876:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var r=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,c=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),u=l(n),m=i,h=u["".concat(c,".").concat(m)]||u[m]||p[m]||a;return n?r.createElement(h,o(o({ref:t},d),{},{components:n})):r.createElement(h,o({ref:t},d))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,o=new Array(a);o[0]=u;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:i,o[1]=s;for(var l=2;l<a;l++)o[l]=n[l];return r.createElement.apply(null,o)}return r.createElement.apply(null,n)}u.displayName="MDXCreateElement"},7850:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var r=n(7896),i=(n(2784),n(876));const a={sidebar_position:2},o="LiveView Paradigm",s={unversionedId:"overview/paradigm",id:"overview/paradigm",title:"LiveView Paradigm",description:"The LiveView model is simple. The server renders an HTML page when a user makes the initial HTTP request. That page",source:"@site/docs/01-overview/paradigm.md",sourceDirName:"01-overview",slug:"/overview/paradigm",permalink:"/docs/overview/paradigm",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Introduction",permalink:"/docs/overview/introduction"},next:{title:"Packages & Runtimes",permalink:"/docs/overview/runtimes"}},c={},l=[{value:"How is this different from SPAs?",id:"how-is-this-different-from-spas",level:2}],d={toc:l};function p(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveview-paradigm"},"LiveView Paradigm"),(0,i.kt)("p",null,"The LiveView model is simple. The server renders an HTML page when a user makes the initial HTTP request. That page\nthen connects to the server via a persistent web socket. From there, user-initiated events (clicks, form input, key\nevents, focus/blur events, etc) are sent over the web socket to the server in very small packets. When the server\nreceives the events, it runs the business logic for that LiveView, calculates the new rendered HTML, and then sends only\nthe diffs to the client. The client automatically updates the page with the diffs. The server can also send diffs back\nto the client based on events on the server or received from other clients (think chat, or other pub/sub scenarios)."),(0,i.kt)("admonition",{type:"info"},(0,i.kt)("p",{parentName:"admonition"},(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," solves the complex parts of LiveViews such as connecting and managing web sockets, diffing and\npatching the UI, routing events, real-time/multiplayer, file uploads, and more.")),(0,i.kt)("h2",{id:"how-is-this-different-from-spas"},"How is this different from SPAs?"),(0,i.kt)("p",null,"SPA-frameworks (React, Vue, Svelt, etc) only manage state and rendering on the client. You need a completely different\nbackend to handle business logic and persistence, typically a REST or GRAPHQL API (and related auth). This means you\nneed to write two code bases, one for the front-end and one for the back-end and then integrate them. ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," is\na single code base that handles both the front-end and back-end while enabling the same rich user experiences that a\nSPA enables. With ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," you write your business logic and persistence code in the same place as your front-end\ncode. This greatly simplifies the development process, reduces the number of moving parts, and increases development velocity."),(0,i.kt)("p",null,"It's worth re-reading Chris McCord's quote in ",(0,i.kt)("a",{parentName:"p",href:"introduction"},"the Introduction"),", or even better, read these docs and run the\nexamples! \ud83d\ude00 You'll see how easy it is to build rich, interactive, and responsive user experiences with ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS"),"\nand start to understand how much of an improvement and paradigm shift it is."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/d9f32620.2fdf6156.js b/docs/assets/js/d9f32620.2fdf6156.js deleted file mode 100644 index 43c95bc6..00000000 --- a/docs/assets/js/d9f32620.2fdf6156.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[1914],{876:(e,t,o)=>{o.d(t,{Zo:()=>c,kt:()=>m});var r=o(2784);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function a(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function l(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?a(Object(o),!0).forEach((function(t){n(e,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):a(Object(o)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))}))}return e}function u(e,t){if(null==e)return{};var o,r,n=function(e,t){if(null==e)return{};var o,r,n={},a=Object.keys(e);for(r=0;r<a.length;r++)o=a[r],t.indexOf(o)>=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)o=a[r],t.indexOf(o)>=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var i=r.createContext({}),s=function(e){var t=r.useContext(i),o=t;return e&&(o="function"==typeof e?e(t):l(l({},t),e)),o},c=function(e){var t=s(e.components);return r.createElement(i.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,a=e.originalType,i=e.parentName,c=u(e,["components","mdxType","originalType","parentName"]),f=s(o),m=n,d=f["".concat(i,".").concat(m)]||f[m]||p[m]||a;return o?r.createElement(d,l(l({ref:t},c),{},{components:o})):r.createElement(d,l({ref:t},c))}));function m(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var a=o.length,l=new Array(a);l[0]=f;var u={};for(var i in t)hasOwnProperty.call(t,i)&&(u[i]=t[i]);u.originalType=e,u.mdxType="string"==typeof e?e:n,l[1]=u;for(var s=2;s<a;s++)l[s]=o[s];return r.createElement.apply(null,l)}return r.createElement.apply(null,o)}f.displayName="MDXCreateElement"},8446:(e,t,o)=>{o.r(t),o.d(t,{assets:()=>i,contentTitle:()=>l,default:()=>p,frontMatter:()=>a,metadata:()=>u,toc:()=>s});var r=o(7896),n=(o(2784),o(876));const a={slug:"welcome",title:"Welcome",authors:["floodfx"],tags:["facebook","hello","docusaurus"]},l=void 0,u={permalink:"/blog/welcome",source:"@site/blog/2021-08-26-welcome/index.md",title:"Welcome",description:"Docusaurus blogging features are powered by the",date:"2021-08-26T00:00:00.000Z",formattedDate:"August 26, 2021",tags:[{label:"facebook",permalink:"/blog/tags/facebook"},{label:"hello",permalink:"/blog/tags/hello"},{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:.405,hasTruncateMarker:!1,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"welcome",title:"Welcome",authors:["floodfx"],tags:["facebook","hello","docusaurus"]},nextItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"}},i={authorsImageUrls:[void 0]},s=[],c={toc:s};function p(e){let{components:t,...a}=e;return(0,n.kt)("wrapper",(0,r.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/blog"},"Docusaurus blogging features")," are powered by the\n",(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog"},"blog plugin"),"."),(0,n.kt)("p",null,"Simply add Markdown files (or folders) to the ",(0,n.kt)("inlineCode",{parentName:"p"},"blog")," directory."),(0,n.kt)("p",null,"Regular blog authors can be added to ",(0,n.kt)("inlineCode",{parentName:"p"},"authors.yml"),"."),(0,n.kt)("p",null,"The blog post date can be extracted from filenames, such as:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"2019-05-30-welcome.md")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"2019-05-30-welcome/index.md"))),(0,n.kt)("p",null,"A blog post folder can be convenient to co-locate blog post images:"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Docusaurus Plushie",src:o(6070).Z,width:"1500",height:"500"})),(0,n.kt)("p",null,"The blog supports tags as well!"),(0,n.kt)("p",null,(0,n.kt)("strong",{parentName:"p"},"And if you don't want a blog"),": just delete this directory, and use ",(0,n.kt)("inlineCode",{parentName:"p"},"blog: false")," in your Docusaurus config."))}p.isMDXComponent=!0},6070:(e,t,o)=>{o.d(t,{Z:()=>r});const r=o.p+"assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg"}}]); \ No newline at end of file diff --git a/docs/assets/js/dddb404b.abf300d4.js b/docs/assets/js/dddb404b.abf300d4.js deleted file mode 100644 index bfef10a1..00000000 --- a/docs/assets/js/dddb404b.abf300d4.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6011],{876:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var o=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function l(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function r(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=o.createContext({}),p=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},m=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},c=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,s=e.parentName,m=r(e,["components","mdxType","originalType","parentName"]),c=p(n),h=i,u=c["".concat(s,".").concat(h)]||c[h]||d[h]||a;return n?o.createElement(u,l(l({ref:t},m),{},{components:n})):o.createElement(u,l({ref:t},m))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,l=new Array(a);l[0]=c;var r={};for(var s in t)hasOwnProperty.call(t,s)&&(r[s]=t[s]);r.originalType=e,r.mdxType="string"==typeof e?e:i,l[1]=r;for(var p=2;p<a;p++)l[p]=n[p];return o.createElement.apply(null,l)}return o.createElement.apply(null,n)}c.displayName="MDXCreateElement"},6090:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>d,frontMatter:()=>a,metadata:()=>r,toc:()=>p});var o=n(7896),i=(n(2784),n(876));const a={sidebar_position:4},l="Show/Hide/Toggle Element Commands",r={unversionedId:"js-commands/show-hide-toggle-el",id:"js-commands/show-hide-toggle-el",title:"Show/Hide/Toggle Element Commands",description:"The show, hide, and toggle commands are used to show, hide, or toggle the visibility of an element including css",source:"@site/docs/11-js-commands/show-hide-toggle-el.md",sourceDirName:"11-js-commands",slug:"/js-commands/show-hide-toggle-el",permalink:"/docs/js-commands/show-hide-toggle-el",draft:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Set/Remove Attribute Commands",permalink:"/docs/js-commands/set-remove-attr"},next:{title:"Dispatch Command",permalink:"/docs/js-commands/dispatch-cmd"}},s={},p=[{value:"Show Command",id:"show-command",level:2},{value:"Hide Command",id:"hide-command",level:2},{value:"Toggle Command",id:"toggle-command",level:2}],m={toc:p};function d(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"showhidetoggle-element-commands"},"Show/Hide/Toggle Element Commands"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"show"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"hide"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"toggle")," commands are used to show, hide, or toggle the visibility of an element including css\ntransition classes. The element is identified by a CSS selector."),(0,i.kt)("h2",{id:"show-command"},"Show Command"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"show")," command makes the target element visible"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().show(options?: ShowOptions)\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element make visible. Defaults to the element that the JS Command is attached\nto."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"time")," - The time (in milliseconds) over which to apply the transition options. Defaults to 200"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"transition")," - The string of classes to apply before showing the element, or a 3-tuple containing the transition\nclass, the class to apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out\nduration-300", "opacity-0", "opacity-100"]'),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"display"),' - The display type to apply to the element. Defaults to "block"')))),(0,i.kt)("p",null,"Examples"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// show the target element on click\n<button phx-click="${new JS().show({ to: "#show_me" })}">Show</button>\n<div id="show_me" style="display: none;">Show Me</div>\n\n// show the target element with a transition on click\n<button phx-click="${new JS().show({\n to: "#show_me2",\n transition: ["ease-out duration-300", "opacity-0", "opacity-100"],\n time: 400\n})}">Show w/ Transition</button>\n<div id="show_me2" style="display: none;">Show Me2</div>\n')),(0,i.kt)("h2",{id:"hide-command"},"Hide Command"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"hide")," command makes the target element hidden"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().hide(options?: ShowOptions)\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element to hide. Defaults to the element that the JS Command is attached to."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"time")," - The time (in milliseconds) over which to apply the transition options. Defaults to 200"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"transition")," - The string of classes to apply before hiding the element, or a 3-tuple containing the transition\nclass, the class to apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out\nduration-300", "opacity-100", "opacity-0"]')))),(0,i.kt)("p",null,"Examples"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// hide the target element on click\n<button phx-click="${new JS().hide({ to: "#hide_me" })}">Hide</button>\n<div id="hide_me">Hide Me</div>\n\n// hide the target element with a transition on click\n<button phx-click="${new JS().hide({\n to: "#hide_me",\n transition: ["ease-out duration-300", "opacity-100", "opacity-0"],\n time: 400\n})}">Hide w/ Transition</button>\n<div id="hide_me2">Hide Me2</div>\n')),(0,i.kt)("h2",{id:"toggle-command"},"Toggle Command"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"toggle")," command toggles the visibility of the target element"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"new JS().toggle(options?: ToggleOptions)\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"options")," - Options for the command (optional)",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"to")," - A css selector to identify the element to toggle. Defaults to the element that the JS Command is attached to."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"time")," - The time (in milliseconds) over which to apply the transition options. Defaults to 200"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"in")," - The string of classes to apply when toggling to visible, or a 3-tuple containing the transition class, the\nclass to apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out duration-300",\n"opacity-0", "opacity-100"]'),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"out")," - The string of classes to apply when toggling to hidden, or a 3-tuple containing the transition class, the\nclass to apply to start the transition, and the class to apply to end the transition. e.g., ",'["ease-out duration-300",\n"opacity-100", "opacity-0"]'),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"display"),' - The display type to apply to the element when toggling to visible. Defaults to "block"')))),(0,i.kt)("p",null,"Examples"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'//... in your render function of a LiveView\n\n// toggle the target element on click\n<button phx-click="${new JS().toggle({ to: "#toggle_me" })}">Toggle</button>\n<div id="toggle_me">Toggler</div>\n\n// toggle the target element with a transition in/out on click\n<button phx-click="${new JS().toggle({\n to: "#toggle_me2",",\n in: ["ease-out duration-300", "opacity-0", "opacity-100"],\n out: ["ease-out duration-300", "opacity-100", "opacity-0"],\n time: 400\n})}">Toggle w/ Transition</button>\n<div id="toggle_me2">Toggle Me 2</div>\n')))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/e16015ca.383724d8.js b/docs/assets/js/e16015ca.383724d8.js deleted file mode 100644 index 28b867e7..00000000 --- a/docs/assets/js/e16015ca.383724d8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9700],{5688:e=>{e.exports=JSON.parse('{"label":"hola","permalink":"/blog/tags/hola","allTagsPath":"/blog/tags","count":1}')}}]); \ No newline at end of file diff --git a/docs/assets/js/e273c56f.1d51bf07.js b/docs/assets/js/e273c56f.1d51bf07.js deleted file mode 100644 index 87b13bda..00000000 --- a/docs/assets/js/e273c56f.1d51bf07.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2362],{876:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>f});var o=r(2784);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function l(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){n(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function a(e,t){if(null==e)return{};var r,o,n=function(e,t){if(null==e)return{};var r,o,n={},i=Object.keys(e);for(o=0;o<i.length;o++)r=i[o],t.indexOf(r)>=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o<i.length;o++)r=i[o],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=o.createContext({}),u=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},c=function(e){var t=u(e.components);return o.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,i=e.originalType,s=e.parentName,c=a(e,["components","mdxType","originalType","parentName"]),m=u(r),f=n,g=m["".concat(s,".").concat(f)]||m[f]||p[f]||i;return r?o.createElement(g,l(l({ref:t},c),{},{components:r})):o.createElement(g,l({ref:t},c))}));function f(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=r.length,l=new Array(i);l[0]=m;var a={};for(var s in t)hasOwnProperty.call(t,s)&&(a[s]=t[s]);a.originalType=e,a.mdxType="string"==typeof e?e:n,l[1]=a;for(var u=2;u<i;u++)l[u]=r[u];return o.createElement.apply(null,l)}return o.createElement.apply(null,r)}m.displayName="MDXCreateElement"},4211:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>p,frontMatter:()=>i,metadata:()=>a,toc:()=>u});var o=r(7896),n=(r(2784),r(876));const i={slug:"first-blog-post",title:"First Blog Post",authors:["floodfx"],tags:["hola","docusaurus"]},l=void 0,a={permalink:"/blog/first-blog-post",source:"@site/blog/2019-05-28-first-blog-post.md",title:"First Blog Post",description:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum",date:"2019-05-28T00:00:00.000Z",formattedDate:"May 28, 2019",tags:[{label:"hola",permalink:"/blog/tags/hola"},{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:.12,hasTruncateMarker:!1,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"first-blog-post",title:"First Blog Post",authors:["floodfx"],tags:["hola","docusaurus"]},prevItem:{title:"Long Blog Post",permalink:"/blog/long-blog-post"}},s={authorsImageUrls:[void 0]},u=[],c={toc:u};function p(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum\ntempor eros aliquam consequat. Lorem ipsum dolor sit amet"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/e53ecdb2.921f2dab.js b/docs/assets/js/e53ecdb2.921f2dab.js deleted file mode 100644 index cf90c847..00000000 --- a/docs/assets/js/e53ecdb2.921f2dab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9484],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>v});var i=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,i,a=function(e,t){if(null==e)return{};var n,i,a={},r=Object.keys(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(i=0;i<r.length;i++)n=r[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=i.createContext({}),s=function(e){var t=i.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=s(e.components);return i.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=s(n),v=a,u=d["".concat(p,".").concat(v)]||d[v]||m[v]||r;return n?i.createElement(u,o(o({ref:t},c),{},{components:n})):i.createElement(u,o({ref:t},c))}));function v(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,o=new Array(r);o[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var s=2;s<r;s++)o[s]=n[s];return i.createElement.apply(null,o)}return i.createElement.apply(null,n)}d.displayName="MDXCreateElement"},2964:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>m,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var i=n(7896),a=(n(2784),n(876));const r={sidebar_position:1},o="LiveView API",l={unversionedId:"anatomy-of-a-liveview/liveview-api",id:"anatomy-of-a-liveview/liveview-api",title:"LiveView API",description:"We are going to be using Typescript in our examples because LiveViewJS is very thoroughly typed, which",source:"@site/docs/03-anatomy-of-a-liveview/liveview-api.md",sourceDirName:"03-anatomy-of-a-liveview",slug:"/anatomy-of-a-liveview/liveview-api",permalink:"/docs/anatomy-of-a-liveview/liveview-api",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Anatomy of a LiveView",permalink:"/docs/category/anatomy-of-a-liveview"},next:{title:"LiveView API - `mount`",permalink:"/docs/anatomy-of-a-liveview/mount-details"}},p={},s=[{value:"LiveView API is Five Methods",id:"liveview-api-is-five-methods",level:2},{value:"Example LiveView Implementation",id:"example-liveview-implementation",level:2},{value:"<code>createLiveView</code> helper",id:"createliveview-helper",level:2},{value:"<code>html</code> helper",id:"html-helper",level:2}],c={toc:s};function m(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"liveview-api"},"LiveView API"),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"We are going to be using Typescript in our examples because ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," is very thoroughly typed, which\nprovides great type hints, autocompletion, etc. If the typescript syntax is confusing, just ignore it and focus on the\ncode.")),(0,a.kt)("h2",{id:"liveview-api-is-five-methods"},"LiveView API is Five Methods"),(0,a.kt)("p",null,"The ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," API is extremely simple but very flexible. There are only five methods that make up the LiveView API:\n",(0,a.kt)("inlineCode",{parentName:"p"},"mount"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"render"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"handleParams"),". Technically, only ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," is required. The other\nfour methods (",(0,a.kt)("inlineCode",{parentName:"p"},"mount"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleParams"),") are optional but usually ",(0,a.kt)("inlineCode",{parentName:"p"},"mount")," and at least one\nother ",(0,a.kt)("inlineCode",{parentName:"p"},"handle")," method is defined to enable a dynamic experience."),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"The smallest, valid LiveView only defines ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," like so:"),(0,a.kt)("pre",{parentName:"admonition"},(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const helloLiveView = createLiveView({\n render: () => html`Hello World`,\n});\n")),(0,a.kt)("p",{parentName:"admonition"},'While "valid" a LiveView like this is not very useful nor particularly exciting. Let\'s look at a more useful example.')),(0,a.kt)("h2",{id:"example-liveview-implementation"},"Example LiveView Implementation"),(0,a.kt)("p",null,"It's helpful to look at a simple LiveView example to see how the LiveView API works. Here is a simple LiveView that\nrenders a counter and has buttons to increment and decrement the counter:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="counterLiveView.ts"',title:'"counterLiveView.ts"'},'import { createLiveView, html } from "liveviewjs";\n/**\n * A basic counter that increments and decrements a number.\n */\nexport const counterLiveView = createLiveView<\n { count: number }, // Define LiveView Context / State\n { type: "increment" } | { type: "decrement" } // Define LiveView Events\n>({\n // Setup / initialize the LiveView Context (i.e., set count to 0)\n mount: (socket) => {\n socket.assign({ count: 0 });\n },\n // Handle incoming increment and decrement events from User input\n handleEvent: (event, socket) => {\n const { count } = socket.context;\n switch (event.type) {\n case "increment":\n socket.assign({ count: count + 1 });\n break;\n case "decrement":\n socket.assign({ count: count - 1 });\n break;\n }\n },\n // Renders the Counter View based on the current Context / State\n render: (context) => {\n const { count } = context;\n return html`\n <div>\n <h1>Count is: ${count}</h1>\n <button phx-click="decrement">-</button>\n <button phx-click="increment">+</button>\n </div>\n `;\n },\n});\n')),(0,a.kt)("p",null,"Before we look at the five LiveView methods a very quick aside on the first line of the example above:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { createLiveView, html } from "liveviewjs";\n')),(0,a.kt)("h2",{id:"createliveview-helper"},(0,a.kt)("inlineCode",{parentName:"h2"},"createLiveView")," helper"),(0,a.kt)("p",null,"LiveViewJS provides various helpers to create and implement LiveViews. The ",(0,a.kt)("inlineCode",{parentName:"p"},"createLiveView")," function is the canonical\nway to define the various functions that make up a LiveView (",(0,a.kt)("inlineCode",{parentName:"p"},"mount"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," etc) and supports typing the LiveView via\nTypescript annotations ",(0,a.kt)("inlineCode",{parentName:"p"},"TContext"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"TEvents"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"TInfo"),"."),(0,a.kt)("h2",{id:"html-helper"},(0,a.kt)("inlineCode",{parentName:"h2"},"html")," helper"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"html")," function is a tagged template literal that allows you to write HTML with dynamic content in a very normal way\nusing javascript\n",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals"},"template literals"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"html")," tag\nhandles escaping content to prevent injection attacks but just as importantly, the ",(0,a.kt)("inlineCode",{parentName:"p"},"html")," tag also\n(transparently) creates the data structure necessary to efficiently calculate diffs between the current HTML and the new\nHTML. This is what allows ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," to efficiently update the DOM with only the changes."),(0,a.kt)("p",null,"Let's take a closer look at ",(0,a.kt)("inlineCode",{parentName:"p"},"mount"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"render"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"handleInfo"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"handleParams")," methods in the next\nsections."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/e96d4430.d98ef097.js b/docs/assets/js/e96d4430.d98ef097.js deleted file mode 100644 index 93b37f04..00000000 --- a/docs/assets/js/e96d4430.d98ef097.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[4379],{876:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>u});var o=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,o,a=function(e,t){if(null==e)return{};var n,o,a={},r=Object.keys(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=o.createContext({}),s=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},d=function(e){var t=s(e.components);return o.createElement(p.Provider,{value:t},e.children)},h={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},c=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=s(n),u=a,m=c["".concat(p,".").concat(u)]||c[u]||h[u]||r;return n?o.createElement(m,i(i({ref:t},d),{},{components:n})):o.createElement(m,i({ref:t},d))}));function u(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=c;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s<r;s++)i[s]=n[s];return o.createElement.apply(null,i)}return o.createElement.apply(null,n)}c.displayName="MDXCreateElement"},9647:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>h,frontMatter:()=>r,metadata:()=>l,toc:()=>s});var o=n(7896),a=(n(2784),n(876));const r={sidebar_position:1},i="Overview",l={unversionedId:"file-upload/overview",id:"file-upload/overview",title:"Overview",description:"File uploads are another common feature of web applications. LiveViewJS provides built in support for file uploads,",source:"@site/docs/08-file-upload/overview.md",sourceDirName:"08-file-upload",slug:"/file-upload/overview",permalink:"/docs/file-upload/overview",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Uploading Files",permalink:"/docs/category/uploading-files"},next:{title:"Built-in Image Preview",permalink:"/docs/file-upload/image-preview"}},p={},s=[{value:"Learn by Example",id:"learn-by-example",level:2},{value:"Example LiveView Code",id:"example-liveview-code",level:2},{value:"Configure the upload",id:"configure-the-upload",level:2},{value:"User Interface",id:"user-interface",level:2},{value:"Setup the Form",id:"setup-the-form",level:3},{value:"File Input and Drag and Drop",id:"file-input-and-drag-and-drop",level:3},{value:"Dynamic Help Text",id:"dynamic-help-text",level:3},{value:"Show preview, progress, and cancel for entries",id:"show-preview-progress-and-cancel-for-entries",level:3},{value:"Show errors as well",id:"show-errors-as-well",level:3},{value:"<code>handleEvent</code> Cases",id:"handleevent-cases",level:2},{value:"<code>cancel</code> event",id:"cancel-event",level:3},{value:"<code>save</code> event",id:"save-event",level:3},{value:"Conclusion",id:"conclusion",level:2}],d={toc:s};function h(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,o.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"overview"},"Overview"),(0,a.kt)("p",null,"File uploads are another common feature of web applications. ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," provides built in support for file uploads,\nimage previews, upload progress, drag and drop, error handling, and more. Handling file uploads can be intimidating, but\n",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," makes it easy."),(0,a.kt)("h2",{id:"learn-by-example"},"Learn by Example"),(0,a.kt)("p",null,"We're going to start with a complete example and then walk through the code. The example LiveView allows you to create a\nnew photo album with a name and up to 3 photos."),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"This example is available as part of the ",(0,a.kt)("inlineCode",{parentName:"p"},"packages/examples")," directory in the\n",(0,a.kt)("a",{parentName:"p",href:"https://github.com/floodfx/liveviewjs"},"LiveViewJS repository")," and runs on both the Express (NodeJS) and Oak (Deno)\nservers.")),(0,a.kt)("h2",{id:"example-liveview-code"},"Example LiveView Code"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import {\n createLiveView,\n error_tag,\n form_for,\n html,\n LiveViewChangeset,\n live_file_input,\n live_img_preview,\n mime,\n SingleProcessPubSub,\n submit,\n text_input,\n UploadEntry,\n} from "liveviewjs";\nimport { nanoid } from "nanoid";\nimport { z } from "zod";\nimport { InMemoryChangesetDB } from "../../datastore/InMemory";\n\ntype PhotosContext = {\n photoGroups: PhotoGroup[];\n changeset: LiveViewChangeset<PhotoGroup>;\n};\n\ntype PhotosEvents =\n | { type: "validate"; name: string }\n | { type: "save"; name: string; urls: string[] }\n | { type: "cancel"; config_name: string; ref: string };\n\nexport const photosLiveView = createLiveView<PhotosContext, PhotosEvents>({\n mount: async (socket) => {\n if (socket.connected) {\n // listen to photos topic\n await socket.subscribe(photoGroupTopic);\n }\n // setup the default context\n socket.assign({\n photoGroups: photoGroupStore.list(),\n changeset: photoGroupStore.changeset(),\n });\n // configure the upload constraints\n socket.allowUpload("photos", {\n accept: [".png", ".jpg", ".jpeg", ".gif"], // only allow images\n maxEntries: 3,\n maxFileSize: 10 * 1024 * 1024, // 10MB\n });\n },\n handleEvent: async (event, socket) => {\n switch (event.type) {\n case "validate": {\n // just validate the changeset\n socket.assign({ changeset: photoGroupStore.validate(event) });\n break;\n }\n case "save": {\n // first get the completed file uploads and map them to urls\n // Note: the files are guaranteed to be completed here because\n // save is the event called after all the uploads are complete\n const { completed } = await socket.uploadedEntries("photos");\n\n // set the urls on the event (which was not set via the form)\n event.urls = completed.map(filename);\n\n // attempt to save the photo\n const photoCreate = photoGroupStore.create(event);\n if (!photoCreate.valid) {\n // if the photo is not valid, assign the changeset and return\n // so that the form is re-rendered with the errors\n socket.assign({\n changeset: photoCreate,\n });\n return;\n }\n // Yay! We\'ve successfully saved the photo, so we can consume (i.e., "remove")\n // the uploaded entries from the "photos" upload config\n await socket.consumeUploadedEntries("photos", async (meta, entry) => {\n // we could create thumbnails, scan for viruses, etc.\n // but for now move the data from the temp file (meta.path) to a public directory\n meta.fileSystem.createOrAppendFile(`./public/${filename(entry)}`, meta.path);\n });\n // update the context with new photos and clear the form\n socket.assign({\n photoGroups: photoGroupStore.list(),\n changeset: photoGroupStore.changeset(),\n });\n break;\n }\n case "cancel": {\n const { config_name, ref } = event;\n // remove the uploaded entry from the upload config\n socket.cancelUpload(config_name, ref);\n }\n }\n },\n // Handle broadcast events from the pub/sub subscription for the "photoGroup" topic\n handleInfo: (info, socket) => {\n const { data } = info;\n socket.assign({\n photoGroups: [data],\n changeset: photoGroupStore.changeset(),\n });\n },\n // Render the view\n render: (ctx, meta) => {\n const { photoGroups, changeset } = ctx;\n const { uploads } = meta;\n return html`\n <h2>My Photo Groups</h2>\n\n \x3c!-- Render the form --\x3e\n ${form_for<PhotoGroup>("#", meta.csrfToken, {\n id: "photo-form",\n phx_change: "validate",\n phx_submit: "save",\n })}\n \x3c!-- photo group name input --\x3e\n <div>\n Photo Group Name:\n ${text_input<PhotoGroup>(changeset, "name")}\n ${error_tag<PhotoGroup>(changeset, "name")}\n </div>\n\n <div>\n \x3c!-- file input / drag and drop --\x3e\n <div phx-drop-target="${uploads.photos.ref}" style="border: 2px dashed #ccc; padding: 10px; margin: 10px 0;">\n ${live_file_input(uploads.photos)}\n or drag and drop files here\n </div>\n \x3c!-- help text --\x3e\n <div style="font-size: 10px; padding-bottom: 3rem">\n Add up to ${uploads.photos.maxEntries} photos\n (max ${uploads.photos.maxFileSize / (1024 * 1024)} MB each)\n </div>\n </div>\n\n \x3c!-- any errors from the upload --\x3e\n ${uploads.photos.errors?.map((error) => html`<p class="invalid-feedback">${error}</p>`)}\n\n \x3c!-- render the preview, progress, and cancel button of the selected files --\x3e\n ${uploads.photos.entries.map(renderEntry)}\n\n \x3c!-- submit button --\x3e\n ${submit("Upload", { phx_disable_with: "Saving...", disabled: uploads.photos.errors.length > 0 })}\n </form>\n\n \x3c!-- render the photo groups --\x3e\n <ul id="photo_groups_list" phx-update="prepend">\n ${photoGroups.map(renderPhotoGroup)}\n </ul>\n `;\n },\n});\n\n// Render a preview of the uploaded file with progress bar and cancel button\nfunction renderEntry(entry: UploadEntry) {\n return html`\n <div style="display: flex; align-items: center;">\n <div style="width: 250px; border: 1px solid black; margin: 2rem 0;">${live_img_preview(entry)}</div>\n <div style="display: flex; align-items: center; margin-left: 2rem;">\n <progress\n style="position: relative; top: 8px; width: 150px; height: 1em;"\n value="${entry.progress}"\n max="100"></progress>\n <span style="margin-left: 1rem;">${entry.progress}%</span>\n </div>\n <div style="display: flex; align-items: center;">\n <a style="padding-left: 2rem;" phx-click="cancel" phx-value-config_name="photos" phx-value-ref="${entry.ref}"\n >\ud83d\uddd1</a\n >\n ${entry.errors?.map((error) => html`<p style="padding-left: 1rem;" class="invalid-feedback">${error}</p>`)}\n </div>\n </div>\n `;\n}\n\n// Render a photo group with a list of photos\nfunction renderPhotoGroup(photoGroup: PhotoGroup) {\n return html`<li id="${photoGroup.id}">\n ${photoGroup.urls.map(\n (url, i) => html`\n <h3>${photoGroup.name}(${i + 1})</h3>\n <img src="${url}" />\n `\n )}\n </li>`;\n}\n\n// Define the shape of the Photo type\nconst PhotoGroupSchema = z.object({\n id: z.string().default(nanoid),\n name: z.string().min(1).max(100),\n urls: z.array(z.string()).min(1).default([]),\n});\n\n// Infer the type from the schema\ntype PhotoGroup = z.infer<typeof PhotoGroupSchema>;\n\n// Pubsub topic for photos\nconst photoGroupTopic = "photoGroup";\n\n// InMemory DB for photoGroup that publishes changes to the "photos" topic\nconst photoGroupStore = new InMemoryChangesetDB<PhotoGroup>(PhotoGroupSchema, {\n pubSub: new SingleProcessPubSub(),\n pubSubTopic: photoGroupTopic,\n});\n\n/**\n * `filename` maps the upload entry to a filename based on the mime type of the entry\n * concatenated with the entry\'s uuid\n */\nfunction filename(entry: UploadEntry) {\n const exts = mime.lookupExtensions(entry.client_type);\n const ext = exts.length > 0 ? exts[0] : "bin";\n return `${entry.uuid}.${ext}`;\n}\n')),(0,a.kt)("p",null,"Let's review each part in more detail to understand what's going on."),(0,a.kt)("h2",{id:"configure-the-upload"},"Configure the upload"),(0,a.kt)("p",null,"First, we need to tell LiveView that we want to upload files and we use the ",(0,a.kt)("inlineCode",{parentName:"p"},"socket.allowUpload")," method in ",(0,a.kt)("inlineCode",{parentName:"p"},"mount")," to do\nso:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'mount: (socket) => {\n...\n // configure the upload constraints\n socket.allowUpload("photos", {\n accept: [".png", ".jpg", ".jpeg", ".gif"], // only allow images\n maxEntries: 3,\n maxFileSize: 10 * 1024 * 1024, // 10MB\n });\n...\n}\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"allowUpload")," method takes a ",(0,a.kt)("inlineCode",{parentName:"p"},"config_name")," and an ",(0,a.kt)("inlineCode",{parentName:"p"},"UploadConfig")," object. The ",(0,a.kt)("inlineCode",{parentName:"p"},"config_name")," is used to identify the\nupload config elsewhere in the LiveView lifecycle methods. More details on ",(0,a.kt)("a",{parentName:"p",href:"upload-config-options"},"config options"),"."),(0,a.kt)("h2",{id:"user-interface"},"User Interface"),(0,a.kt)("p",null,"There is a lot going on in our LiveView's ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," function so let's walk through that."),(0,a.kt)("h3",{id:"setup-the-form"},"Setup the Form"),(0,a.kt)("p",null,"As usual, we start by rendering the form with the ",(0,a.kt)("inlineCode",{parentName:"p"},"form_for")," helper and set the ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-change")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-submit")," events to\n",(0,a.kt)("inlineCode",{parentName:"p"},"validate")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"save")," respectively."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\n${form_for<PhotoGroup>("#", meta.csrfToken, {\n id: "photo-form",\n phx_change: "validate",\n phx_submit: "save",\n })}\n...\n')),(0,a.kt)("p",null,"We will look at the ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," method later to see how especially the ",(0,a.kt)("inlineCode",{parentName:"p"},"save")," event is handled."),(0,a.kt)("h3",{id:"file-input-and-drag-and-drop"},"File Input and Drag and Drop"),(0,a.kt)("p",null,"Next, we need a place for the user to choose files for upload. We use the ",(0,a.kt)("inlineCode",{parentName:"p"},"live_file_input")," helper to render the file\ninput and the ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-drop-target")," attribute to make the element a drop target for files. The ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-drop-target")," attribute\ntakes a ",(0,a.kt)("inlineCode",{parentName:"p"},"ref")," which is used to identify the upload config in the LiveView lifecycle methods. You'll notice we are\nreferencing the ",(0,a.kt)("inlineCode",{parentName:"p"},"uploads.photos")," config we configured in ",(0,a.kt)("inlineCode",{parentName:"p"},"mount")," earlier."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\n\x3c!-- file input / drag and drop --\x3e\n<div phx-drop-target="${uploads.photos.ref}" style="border: 2px dashed #ccc; padding: 10px; margin: 10px 0;">\n ${live_file_input(uploads.photos)}\n or drag and drop files here\n</div>\n...\n')),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"\ud83e\udd2f We just added a drag and drop target to our user interface with a single attribute (i.e.\n",(0,a.kt)("inlineCode",{parentName:"p"},'phx-drop-target="${uploads.photos.ref}"'),")! Pretty cool, right!? Thanks Phoenix LiveView team!! \ud83d\ude4c")),(0,a.kt)("admonition",{type:"caution"},(0,a.kt)("p",{parentName:"admonition"},"The ",(0,a.kt)("inlineCode",{parentName:"p"},"live_file_input")," helper goes beyond just rendering the file input, it also adds some required attributes\nto the file input and works with the rest of ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS")," to handle uploads. You should always use it rather than\nrendering the file input yourself.")),(0,a.kt)("h3",{id:"dynamic-help-text"},"Dynamic Help Text"),(0,a.kt)("p",null,"A very nice aspect of having the upload config available in ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," is it allows us to dynamically render help text\nbased on the upload config:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"...\nAdd up to ${uploads.photos.maxEntries} photos\n(max ${uploads.photos.maxFileSize / (1024 * 1024)} MB each)\n...\n")),(0,a.kt)("h3",{id:"show-preview-progress-and-cancel-for-entries"},"Show preview, progress, and cancel for entries"),(0,a.kt)("p",null,"When a user selects (or drags and drops) files for upload, the ",(0,a.kt)("inlineCode",{parentName:"p"},"meta.uploads")," object is automatically updated with those\nentries (and any errors for the upload or entries). We can use the ",(0,a.kt)("inlineCode",{parentName:"p"},"upload.entries")," (and ",(0,a.kt)("inlineCode",{parentName:"p"},"upload.errors"),") to show the\nuser what will be uploaded or what errors in their selections."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"...\n\x3c!-- render the preview, progress, and cancel button of the selected files --\x3e\n${uploads.photos.entries.map(renderEntry)}\n...\n")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"renderEntry")," function shows the image preview using ",(0,a.kt)("inlineCode",{parentName:"p"},"live_img_preview")," and the progress of the upload using a\n",(0,a.kt)("inlineCode",{parentName:"p"},"progress")," element. We also render a cancel button using the ",(0,a.kt)("inlineCode",{parentName:"p"},"phx-click")," event to cancel the upload."),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("mdxAdmonitionTitle",{parentName:"admonition"},"\ud83e\udd2f Since we are allowing images only, we can use the ",(0,a.kt)("inlineCode",{parentName:"mdxAdmonitionTitle"},"live_img_preview")," helper to render a preview of the image"),(0,a.kt)("p",{parentName:"admonition"},"before it is uploaded. Again, pretty amazing that we get an image preview for free! Thanks Phoenix LiveView team!! \ud83d\ude4c")),(0,a.kt)("h3",{id:"show-errors-as-well"},"Show errors as well"),(0,a.kt)("p",null,"We configured the file uploads to only allow certain image file types, limited the number of files to 3, and limited the\nfile size to 10MB. If the user selects files that don't meet these constraints, the ",(0,a.kt)("inlineCode",{parentName:"p"},"uploads")," object will be updated\nwith the errors for the given config. We can use the ",(0,a.kt)("inlineCode",{parentName:"p"},"upload.photos.errors")," to show the user what errors they have made\nfor the upload config and ",(0,a.kt)("inlineCode",{parentName:"p"},"entry.errors")," to show the errors for a given entry."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\n\x3c!-- render the errors for the upload config --\x3e\n${uploads.photos.errors?.map((error) => html`<p class="invalid-feedback">${error}</p>`)}\n...\n')),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\n\x3c!-- render the errors for the entry --\x3e\n${entry.errors?.map((error) => html`<p class="invalid-feedback">${error}</p>`)}\n...\n')),(0,a.kt)("p",null,"Whew, we've got some pretty amazing functionality in our UI and we haven't even uploaded any files yet! Let's look at\nthe LiveView lifecycle methods to see how we handle the uploads."),(0,a.kt)("h2",{id:"handleevent-cases"},(0,a.kt)("inlineCode",{parentName:"h2"},"handleEvent")," Cases"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," has two main events that it is handling for us: ",(0,a.kt)("inlineCode",{parentName:"p"},"cancel"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"save"),". Let's look at each of these in turn."),(0,a.kt)("h3",{id:"cancel-event"},(0,a.kt)("inlineCode",{parentName:"h3"},"cancel")," event"),(0,a.kt)("p",null,"A user may want to remove an entry from the setup of files they have selected. Perhaps the file is too large or the\nwrong type or they've simply changed their mind. Our ",(0,a.kt)("inlineCode",{parentName:"p"},"renderEntry")," function renders a cancel button next to each entry\nthat fires off the ",(0,a.kt)("inlineCode",{parentName:"p"},"cancel")," event enabling the user to remove the entry from the upload."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nhandleEvent: (event, socket) => {\n ...\n case "cancel": {\n const { ref } = event;\n // remove the entry from the upload config\n socket.cancelUpload("photos", ref);\n break;\n }\n ...\n}\n...\n')),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"A user can cancel an upload anytime before the ",(0,a.kt)("inlineCode",{parentName:"p"},"socket.consumeUploadedEntries")," method is called.")),(0,a.kt)("h3",{id:"save-event"},(0,a.kt)("inlineCode",{parentName:"h3"},"save")," event"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"save")," event is automatically fired when the user submits the form. In the case of file uploads, this event is not\nsent to the ",(0,a.kt)("inlineCode",{parentName:"p"},"handleEvent")," method until after all the files have been fully uploaded."),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"The upload progress for each entry will automatically be updated and the ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," method will be executed as\nthey are uploaded allowing us to show the user the progress of the upload.")),(0,a.kt)("p",null,"Let's look at the ",(0,a.kt)("inlineCode",{parentName:"p"},"save")," event handler:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nhandleEvent: (event, socket) => {\n ...\n case "save": {\n // first get the completed file uploads and map them to urls\n // Note: the files are guaranteed to be completed here because\n // save is the event called after all the uploads are complete\n const { completed } = await socket.uploadedEntries("photos");\n\n // set the urls on the event (which was not set via the form)\n event.urls = completed.map(filename);\n\n // attempt to save the photo\n const photoCreate = photoGroupStore.create(event);\n if (!photoCreate.valid) {\n // if the photo is not valid, assign the changeset and return\n // so that the form is re-rendered with the errors\n socket.assign({\n changeset: photoCreate,\n });\n return;\n }\n // Yay! We\'ve successfully saved the photo, so we can consume (i.e., "remove")\n // the uploaded entries from the "photos" upload config\n await socket.consumeUploadedEntries("photos", async (meta, entry) => {\n // we could create thumbnails, scan for viruses, etc.\n // but for now move the data from the temp file (meta.path) to a public directory\n meta.fileSystem.createOrAppendFile(`./public/${filename(entry)}`, meta.path);\n });\n // update the context with new photos and clear the form\n socket.assign({\n photoGroups: photoGroupStore.list(),\n changeset: photoGroupStore.changeset(),\n });\n break;\n }\n ...\n}\n')),(0,a.kt)("p",null,"It's pretty well commented but to summarize:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"We get the completed uploads from the ",(0,a.kt)("inlineCode",{parentName:"li"},"photos")," upload config. (Note: the files are guaranteed to be completed here\nbecause ",(0,a.kt)("inlineCode",{parentName:"li"},"save")," is the event called only after all the uploads are complete)."),(0,a.kt)("li",{parentName:"ol"},"We map each entry to a url and add the ",(0,a.kt)("inlineCode",{parentName:"li"},"urls")," to the ",(0,a.kt)("inlineCode",{parentName:"li"},"event")," (which will become the ",(0,a.kt)("inlineCode",{parentName:"li"},"photoGroup"),")."),(0,a.kt)("li",{parentName:"ol"},"We attempt to save the ",(0,a.kt)("inlineCode",{parentName:"li"},"photoGroup")," and check if the changeset is valid. If not, we return here to show the errors\nrather than ",(0,a.kt)("inlineCode",{parentName:"li"},"consumeUploadedEntries"),"."),(0,a.kt)("li",{parentName:"ol"},"If the changeset is valid, we ",(0,a.kt)("inlineCode",{parentName:"li"},"consumeUploadedEntries")," which will move the files from the temp directory to the\npublic directory and importantly, remove these files from the upload config."),(0,a.kt)("li",{parentName:"ol"},"Finally, We update the ",(0,a.kt)("inlineCode",{parentName:"li"},"context")," and clear the form.")),(0,a.kt)("h2",{id:"conclusion"},"Conclusion"),(0,a.kt)("p",null,"Thanks for sticking with us through that. It was long and detailed and hopefully it was helpful. We think ",(0,a.kt)("strong",{parentName:"p"},"LiveViewJS"),"\nprovides some pretty amazing out of the box support for file uploads."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/eb3e9177.8cc4ebe8.js b/docs/assets/js/eb3e9177.8cc4ebe8.js deleted file mode 100644 index 56b00cdb..00000000 --- a/docs/assets/js/eb3e9177.8cc4ebe8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[653],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>u});var o=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},r=Object.keys(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o<r.length;o++)n=r[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=o.createContext({}),p=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},c=function(e){var t=p(e.components);return o.createElement(s.Provider,{value:t},e.children)},h={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=p(n),u=i,m=d["".concat(s,".").concat(u)]||d[u]||h[u]||r;return n?o.createElement(m,a(a({ref:t},c),{},{components:n})):o.createElement(m,a({ref:t},c))}));function u(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,a=new Array(r);a[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var p=2;p<r;p++)a[p]=n[p];return o.createElement.apply(null,a)}return o.createElement.apply(null,n)}d.displayName="MDXCreateElement"},5119:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>a,default:()=>h,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var o=n(7896),i=(n(2784),n(876));const r={sidebar_position:2},a='"Hooks" (not the React kind)',l={unversionedId:"client-javascript/client-hooks",id:"client-javascript/client-hooks",title:'"Hooks" (not the React kind)',description:"Sometimes you need to do something that is not supported by any of the existing user event bindings or that requires",source:"@site/docs/10-client-javascript/client-hooks.md",sourceDirName:"10-client-javascript",slug:"/client-javascript/client-hooks",permalink:"/docs/client-javascript/client-hooks",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Client-side Javascript",permalink:"/docs/client-javascript/overview"},next:{title:"Example Hook",permalink:"/docs/client-javascript/example-hook"}},s={},p=[{value:"<code>phx-hook</code> Attribute",id:"phx-hook-attribute",level:2},{value:"Registering Hooks",id:"registering-hooks",level:2},{value:"Hook Lifecycle",id:"hook-lifecycle",level:2},{value:"Hook Context",id:"hook-context",level:2}],c={toc:p};function h(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"hooks-not-the-react-kind"},'"Hooks" (not the React kind)'),(0,i.kt)("p",null,'Sometimes you need to do something that is not supported by any of the existing user event bindings or that requires\nhooking into a client-side DOM event. LiveView has "Hooks" for these types of situations.'),(0,i.kt)("admonition",{type:"caution"},(0,i.kt)("p",{parentName:"admonition"},'The term "Hooks" comes from Phoenix/LiveView which this project is based on and whose client library we are\nutilizing. LiveView "Hooks" are in no way related to React "Hooks". It is unfortunate that "Hooks" is overloaded but we\ndon\'t find it very confusing considering how different LiveView is from React.')),(0,i.kt)("h2",{id:"phx-hook-attribute"},(0,i.kt)("inlineCode",{parentName:"h2"},"phx-hook")," Attribute"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"phx-hook"),' attribute is used to attach a LiveView "Hook" to a DOM element. The value of the attribute is the name of\nthe hook (which must be registered in the client-side ',(0,i.kt)("inlineCode",{parentName:"p"},"LiveSocket"),"). For example, if you wanted to attach a hook named\n",(0,i.kt)("inlineCode",{parentName:"p"},"MyHook")," to a button, you would do the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'<button phx-hook="MyHook">Click Me</button>\n')),(0,i.kt)("h2",{id:"registering-hooks"},"Registering Hooks"),(0,i.kt)("p",null,"As noted above, you must register the hook in the client-side ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveSocket")," before you can use it. You do this by first\ndefining the hook and then by adding the hook to the ",(0,i.kt)("inlineCode",{parentName:"p"},"hooks")," option when you create the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveSocket"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// Define the hook\nconst Hooks = {\n MyHook: {\n mounted() {\n // do something when the element is mounted\n console.log("MyHook mounted");\n }\n }\n}\n...\n// Add the hook to the LiveSocket\nlet liveSocket = new LiveSocket("/live", Socket, {\n hooks: { MyHook }\n});\n...\n')),(0,i.kt)("p",null,"This hook will simply print a message to the console when the element is mounted."),(0,i.kt)("h2",{id:"hook-lifecycle"},"Hook Lifecycle"),(0,i.kt)("p",null,"Hooks have the following lifecycle methods:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mounted")," - the element has been added to the DOM and its server LiveView has finished mounting"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"beforeUpdate")," - the element is about to be updated in the DOM. Note: any call here must be synchronous as the\noperation cannot be deferred or cancelled."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"updated")," - the element has been updated in the DOM by the server"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"destroyed")," - the element has been removed from the page, either by a parent update, or by the parent being removed\nentirely"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"disconnected")," - the element's parent LiveView has disconnected from the server"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"reconnected")," - the element's parent LiveView has reconnected to the server")),(0,i.kt)("h2",{id:"hook-context"},"Hook Context"),(0,i.kt)("p",null,"Inside the hook lifecycle methods you can reference many additional properties and methods for the hook including:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"el")," - attribute referencing the bound DOM node liveSocket - the reference to the underlying LiveSocket instance"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pushEvent(event, payload, (reply, ref) => ...)")," - method to push an event from the client to the LiveView server"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pushEventTo(selectorOrTarget, event, payload, (reply, ref) => ...)")," - method to push targeted events from the client\nto LiveViews and LiveComponents. It sends the event to the LiveComponent or LiveView the selectorOrTarget is defined\nin, where its value can be either a query selector or an actual DOM element. If the query selector returns more than\none element it will send the event to all of them, even if all the elements are in the same LiveComponent or LiveView."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"handleEvent(event, (payload) => ...)")," - method to handle an event pushed from the server"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"upload(name, files)")," - method to inject a list of file-like objects into an uploader."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"uploadTo(selectorOrTarget, name, files)")," - method to inject a list of file-like objects into an uploader. The hook\nwill send the files to the uploader with name defined by allow_upload/3 on the server-side. Dispatching new uploads\ntriggers an input change event which will be sent to the LiveComponent or LiveView the selectorOrTarget is defined in,\nwhere its value can be either a query selector or an actual DOM element. If the query selector returns more than one\nlive file input, an error will be logged.")),(0,i.kt)("admonition",{type:"tip"},(0,i.kt)("p",{parentName:"admonition"},"The ",(0,i.kt)("inlineCode",{parentName:"p"},"@types/phoenix_live_view")," package provides a (currently incomplete) type definition for a hook. You can use\nit by importing the ",(0,i.kt)("inlineCode",{parentName:"p"},"ViewHook")," type from the ",(0,i.kt)("inlineCode",{parentName:"p"},"phoenix_live_view")," package."),(0,i.kt)("pre",{parentName:"admonition"},(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'import { ViewHook } from "phoenix_live_view";\n'))))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/ebc9356c.c0f8c26c.js b/docs/assets/js/ebc9356c.c0f8c26c.js deleted file mode 100644 index f73c9b51..00000000 --- a/docs/assets/js/ebc9356c.c0f8c26c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9991],{3769:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]); \ No newline at end of file diff --git a/docs/assets/js/ed398bd8.068419c2.js b/docs/assets/js/ed398bd8.068419c2.js deleted file mode 100644 index be865533..00000000 --- a/docs/assets/js/ed398bd8.068419c2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[9773],{876:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var o=n(2784);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?l(Object(n),!0).forEach((function(t){a(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):l(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function r(e,t){if(null==e)return{};var n,o,a=function(e,t){if(null==e)return{};var n,o,a={},l=Object.keys(e);for(o=0;o<l.length;o++)n=l[o],t.indexOf(n)>=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(o=0;o<l.length;o++)n=l[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=o.createContext({}),d=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=d(e.components);return o.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},u=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,s=e.parentName,p=r(e,["components","mdxType","originalType","parentName"]),u=d(n),m=a,f=u["".concat(s,".").concat(m)]||u[m]||c[m]||l;return n?o.createElement(f,i(i({ref:t},p),{},{components:n})):o.createElement(f,i({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,i=new Array(l);i[0]=u;var r={};for(var s in t)hasOwnProperty.call(t,s)&&(r[s]=t[s]);r.originalType=e,r.mdxType="string"==typeof e?e:a,i[1]=r;for(var d=2;d<l;d++)i[d]=n[d];return o.createElement.apply(null,i)}return o.createElement.apply(null,n)}u.displayName="MDXCreateElement"},3801:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>c,frontMatter:()=>l,metadata:()=>r,toc:()=>d});var o=n(7896),a=(n(2784),n(876));const l={sidebar_position:5},i="LiveViewSocket API - Uploads",r={unversionedId:"liveview-socket/liveviewsocket-api-uploads",id:"liveview-socket/liveviewsocket-api-uploads",title:"LiveViewSocket API - Uploads",description:"A common use case for a web application is to allow users to upload files. The following methods on the LiveViewSocket",source:"@site/docs/04-liveview-socket/liveviewsocket-api-uploads.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/liveviewsocket-api-uploads",permalink:"/docs/liveview-socket/liveviewsocket-api-uploads",draft:!1,tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket API - Server Events",permalink:"/docs/liveview-socket/liveviewsocket-api-infos"},next:{title:"LiveViewSocket API - Misc",permalink:"/docs/liveview-socket/liveviewsocket-api-misc"}},s={},d=[{value:"LiveViewSocket Properties and Methods",id:"liveviewsocket-properties-and-methods",level:2},{value:"<code>allowUpload</code> Method",id:"allowupload-method",level:2},{value:"<code>cancelUpload</code> Method",id:"cancelupload-method",level:2},{value:"<code>consumeUploadedEntries</code> Method",id:"consumeuploadedentries-method",level:2},{value:"<code>uploadedEntries</code> Method",id:"uploadedentries-method",level:2},{value:"More details",id:"more-details",level:2}],p={toc:d};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,o.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"liveviewsocket-api---uploads"},"LiveViewSocket API - Uploads"),(0,a.kt)("p",null,"A common use case for a web application is to allow users to upload files. The following methods on the ",(0,a.kt)("inlineCode",{parentName:"p"},"LiveViewSocket"),"\nenable you to upload files to your server."),(0,a.kt)("h2",{id:"liveviewsocket-properties-and-methods"},"LiveViewSocket Properties and Methods"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Name"),(0,a.kt)("th",{parentName:"tr",align:null},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"allowUpload(name: string, options?: UploadConfigOptions): Promise<void>;")),(0,a.kt)("td",{parentName:"tr",align:null},"Allows file uploads for the given LiveView and configures the upload options (filetypes, size, etc).")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"cancelUpload(configName: string, ref: string): Promise<void>;")),(0,a.kt)("td",{parentName:"tr",align:null},"Cancels the file upload for a given UploadConfig by config name and file ref.")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"consumeUploadedEntries<T>(configName: string,fn: (meta: ConsumeUploadedEntriesMeta, entry: UploadEntry) => Promise<T>):Promise<T[]>;")),(0,a.kt)("td",{parentName:"tr",align:null},'Consume the uploaded files for a given UploadConfig (by name). This should only be called after the form\'s "save" event has occurred which guarantees all the files for the upload have been fully uploaded.')),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"uploadedEntries(configName: string): Promise<{completed: UploadEntry[];inProgress: UploadEntry[];}>;")),(0,a.kt)("td",{parentName:"tr",align:null},"Returns two sets of files that are being uploaded, those ",(0,a.kt)("inlineCode",{parentName:"td"},"completed")," and those ",(0,a.kt)("inlineCode",{parentName:"td"},"inProgress")," for a given UploadConfig (by name). Unlike ",(0,a.kt)("inlineCode",{parentName:"td"},"consumeUploadedEntries"),', this does not require the form\'s "save" event to have occurred and will not throw if any of the entries are not fully uploaded.')))),(0,a.kt)("h2",{id:"allowupload-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"allowUpload")," Method"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"allowUpload")," is used to configure the file upload options for a given LiveView. This method should be called in the\n",(0,a.kt)("inlineCode",{parentName:"p"},"mount")," method of your LiveView. The ",(0,a.kt)("inlineCode",{parentName:"p"},"options")," parameter is optional and if not provided, the default options will be\nused. ",(0,a.kt)("inlineCode",{parentName:"p"},"allowUpload")," requires a ",(0,a.kt)("inlineCode",{parentName:"p"},"name")," parameter which is used to identify the upload config elsewhere in your code. Here\nis an example of configuring the upload options for a LiveView using ",(0,a.kt)("inlineCode",{parentName:"p"},"allowUpload"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nmount: (socket) => {\n ...\n // configure the upload constraints\n socket.allowUpload("photos", {\n accept: [".png", ".jpg", ".jpeg", ".gif"], // only allow images\n maxEntries: 3, // only allow 3 files to be uploaded\n maxFileSize: 10 * 1024 * 1024, // 10MB\n });\n ...\n}\n...\n')),(0,a.kt)("p",null,"Now that you've configured the upload options, you can use those options to render a ",(0,a.kt)("inlineCode",{parentName:"p"},"live_file_input")," tag that allows\nthe user to upload files. Here is an example of a ",(0,a.kt)("inlineCode",{parentName:"p"},"live_file_input")," tag that uses the ",(0,a.kt)("inlineCode",{parentName:"p"},"photos")," upload config as part of\nyour ",(0,a.kt)("inlineCode",{parentName:"p"},"render")," template:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-html"},'\x3c!-- use the "photos" upload config --\x3e\n<div>${live_file_input(uploads.photos)}</div>\n')),(0,a.kt)("h2",{id:"cancelupload-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"cancelUpload")," Method"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"cancelUpload"),' is a way to remove a file from the set of entries that either are ready to be uploaded or have already\nbeen uploaded (but not fully consumed yet). Typically, you would call this method in response to a user action such as\nclicking a "remove" button next to a file. Here is an example of calling ',(0,a.kt)("inlineCode",{parentName:"p"},"cancelUpload")," in response to a user action:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'handleEvent: (socket, event) => {\n ...\n switch (event.type) {\n ...\n case "cancel":\n const { config_name, ref } = event;\n // remove the uploaded entry from the upload config\n socket.cancelUpload(config_name, ref);\n break;\n ...\n }\n ...\n}\n...\nrender: (context, meta) {\n ...\n <a phx-click="cancel" phx-value-config_name="photos" phx-value-ref="${entry.ref}">\ud83d\uddd1</a>\n ...\n}\n...\n')),(0,a.kt)("h2",{id:"consumeuploadedentries-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"consumeUploadedEntries")," Method"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"consumeUploadedEntries")," is a way to fully process the uploaded files for a given ",(0,a.kt)("inlineCode",{parentName:"p"},"UploadConfig")," (by name). ",(0,a.kt)("strong",{parentName:"p"},'This\nshould only be called after the form\'s "save" event has occurred which guarantees all the files for the upload have been\nfully uploaded'),". Here is an example of calling ",(0,a.kt)("inlineCode",{parentName:"p"},"consumeUploadedEntries"),' after the form\'s "save" event has occurred:'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\nhandleEvent: (socket, event) => {\n ...\n switch (event.type) {\n ...\n case "save":\n ...\n // consume the uploaded entries for the "photos" upload config\n await socket.consumeUploadedEntries("photos", async (meta, entry) => {\n // we could create thumbnails, scan for viruses, etc.\n // but for now move the data from the temp file (meta.path) to a public directory\n meta.fileSystem.createOrAppendFile(`./public/${filename(entry)}`, meta.path);\n });\n ...\n break;\n ...\n }\n ...\n}\n...\n')),(0,a.kt)("h2",{id:"uploadedentries-method"},(0,a.kt)("inlineCode",{parentName:"h2"},"uploadedEntries")," Method"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"uploadedEntries")," returns the set of files that are either in progress of being uploaded or have already been uploaded\n(but not fully consumed yet). Unlike ",(0,a.kt)("inlineCode",{parentName:"p"},"consumeUploadedEntries")," this can be called before the form's save event has\noccured."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'...\n// get the complete and in progress entries for the "photos" upload config\nconst { completed, inProgress } = await socket.uploadedEntries("photos");\n// do something with the entries\n...\n')),(0,a.kt)("h2",{id:"more-details"},"More details"),(0,a.kt)("p",null,"More details on file uploads can be found in the ",(0,a.kt)("a",{parentName:"p",href:"/docs/file-upload/overview"},"File Uploads")," section of the docs."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/f08e994e.f1826f7a.js b/docs/assets/js/f08e994e.f1826f7a.js deleted file mode 100644 index 71a5697f..00000000 --- a/docs/assets/js/f08e994e.f1826f7a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[333],{876:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>v});var o=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?a(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):a(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,o,i=function(e,t){if(null==e)return{};var n,o,i={},a=Object.keys(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)n=a[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=o.createContext({}),s=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},c=function(e){var t=s(e.components);return o.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},d=o.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,p=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=s(n),v=i,u=d["".concat(p,".").concat(v)]||d[v]||m[v]||a;return n?o.createElement(u,r(r({ref:t},c),{},{components:n})):o.createElement(u,r({ref:t},c))}));function v(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,r=new Array(a);r[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,r[1]=l;for(var s=2;s<a;s++)r[s]=n[s];return o.createElement.apply(null,r)}return o.createElement.apply(null,n)}d.displayName="MDXCreateElement"},4969:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>r,default:()=>m,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var o=n(7896),i=(n(2784),n(876));const a={sidebar_position:2},r="Live Components",l={unversionedId:"misc/livecomponents",id:"misc/livecomponents",title:"Live Components",description:"We've mostly been talking about LiveViews, but LiveViewJS also supports LiveComponents. Live Components are a way",source:"@site/docs/13-misc/livecomponents.md",sourceDirName:"13-misc",slug:"/misc/livecomponents",permalink:"/docs/misc/livecomponents",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Security Topics",permalink:"/docs/misc/security-topics"},next:{title:"Statics and Dynamics",permalink:"/docs/misc/statics-and-dynamics"}},p={},s=[{value:"LiveComponent API",id:"livecomponent-api",level:2},{value:"Stateful vs Stateless",id:"stateful-vs-stateless",level:3},{value:"Lifecycle Differences",id:"lifecycle-differences",level:3},{value:"Stateful LiveComponent <code>handleEvent</code>",id:"stateful-livecomponent-handleevent",level:3},{value:"Adding a LiveComponent to a LiveView",id:"adding-a-livecomponent-to-a-liveview",level:2},{value:"LiveComponentSocket API",id:"livecomponentsocket-api",level:2}],c={toc:s};function m(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,o.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"live-components"},"Live Components"),(0,i.kt)("p",null,"We've mostly been talking about LiveViews, but ",(0,i.kt)("strong",{parentName:"p"},"LiveViewJS")," also supports ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveComponents"),". Live Components are a way\nto create self-contained, stateful and stateless components that are reusable across multiple LiveViews. Live Components\nare a great way to break up your LiveView into smaller, reusable, more manageable pieces."),(0,i.kt)("h2",{id:"livecomponent-api"},"LiveComponent API"),(0,i.kt)("p",null,"LiveComponents have a much simpler API than LiveViews. They have the following methods all of which are optional except\nfor ",(0,i.kt)("inlineCode",{parentName:"p"},"render"),":"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"mount")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"update")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"handleEvent")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"render")," (required)")),(0,i.kt)("h3",{id:"stateful-vs-stateless"},"Stateful vs Stateless"),(0,i.kt)("p",null,"LiveComponents can be stateful or stateless. Stateful components are identified by passing an ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," property into the\n",(0,i.kt)("inlineCode",{parentName:"p"},"meta.live_component")," function in the LiveView. Stateless LiveComponents do not have an ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," property. Stateful\ncomponents are also the only type of LiveComponent that can receive events (and therefore have a ",(0,i.kt)("inlineCode",{parentName:"p"},"handleEvent")," method)."),(0,i.kt)("h3",{id:"lifecycle-differences"},"Lifecycle Differences"),(0,i.kt)("p",null,"Both types of LiveComponents have the same lifecycle methods. Both types initially follow the same execution flow when\nthey are first loaded:"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"mount => update => render")),(0,i.kt)("p",null,"With Stateless LiveComponents, the execution flow above is the same for every render cycle. For Stateful LiveComponents,\nafter the first render cycle, the execution flow is:"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"update => render")),(0,i.kt)("h3",{id:"stateful-livecomponent-handleevent"},"Stateful LiveComponent ",(0,i.kt)("inlineCode",{parentName:"h3"},"handleEvent")),(0,i.kt)("p",null,"Targeting a LiveComponent requires the addition of a ",(0,i.kt)("inlineCode",{parentName:"p"},"phx-target")," attribute to a the rendered HTML element. Inside of a\n",(0,i.kt)("inlineCode",{parentName:"p"},"render")," a LiveComponent can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"meta.myself")," property in the ",(0,i.kt)("inlineCode",{parentName:"p"},"phx-target")," attribute to target itself. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'<button phx-click="my_event" phx-target="${meta.myself}">Click Me</button>\n')),(0,i.kt)("p",null,"Alternatively, you can target another LiveComponent by passing the DOM id or class selector into the ",(0,i.kt)("inlineCode",{parentName:"p"},"phx-target"),"\nattribute. For example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'<button phx-click="my_event" phx-target="#comp_3">Click Me</button>\n')),(0,i.kt)("p",null,"In either case, the ",(0,i.kt)("inlineCode",{parentName:"p"},"handleEvent")," method will be called with the ",(0,i.kt)("inlineCode",{parentName:"p"},"my_event")," event prompting a re-render of the\nLiveComponent."),(0,i.kt)("h2",{id:"adding-a-livecomponent-to-a-liveview"},"Adding a LiveComponent to a LiveView"),(0,i.kt)("p",null,"To add a LiveComponent to a LiveView, you use the ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewMeta")," ",(0,i.kt)("inlineCode",{parentName:"p"},"live_component")," function. The ",(0,i.kt)("inlineCode",{parentName:"p"},"live_component"),"\nfunction takes a LiveComponent along with a JSON object with an optional ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," property. If the ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," property is present,\nthe LiveComponent will be stateful. If the ",(0,i.kt)("inlineCode",{parentName:"p"},"id")," property is not present, the LiveComponent will be stateless. For\nexample:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'...\nrender: (context, meta) => {\n return html`\n <div>\n ${meta.live_component(MyStatefulComponent, {id: "comp_1", bar: "baz"})}\n ${meta.live_component(MyStatefulComponent, {id: "comp_2"})}\n ${meta.live_component(MyStatelessComponent, {foo: "bar"})}\n </div>\n `\n}\n...\n')),(0,i.kt)("h2",{id:"livecomponentsocket-api"},"LiveComponentSocket API"),(0,i.kt)("p",null,"Similar to LiveViews, LiveComponents have a ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveComponentSocket")," API that is the utility belt for LiveComponents. Below\nis the API for LiveComponentSocket:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Represents the `LiveComponent`'s websocket connectedness along with current\n * state of the component. Also provides a method for sending messages\n * internally to the parent `LiveView`.\n */\nexport interface LiveComponentSocket<\n TContext extends LiveContext = AnyLiveContext,\n TInfo extends LiveInfo = AnyLiveInfo\n> {\n /**\n * The id of the parent `LiveView`\n */\n id: string;\n /**\n * Whether the websocket is connected (i.e., http request or joined via websocket)\n * true if connected to a websocket, false for http request\n */\n connected: boolean;\n /**\n * Read-only, current state of the `LiveComponent`\n */\n context: TContext;\n /**\n * helper method to send messages to the parent `LiveView` via the `handleInfo`\n */\n sendParentInfo(info: Info<TInfo>): void;\n /**\n * `assign` is used to update the `Context` (i.e., state) of the `LiveComponent`\n */\n assign(context: Partial<TContext>): void;\n /**\n * helper method to send events to Hooks on the parent `LiveView`\n */\n pushEvent(pushEvent: AnyLivePushEvent): void;\n}\n")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/f4424813.c1da1dc0.js b/docs/assets/js/f4424813.c1da1dc0.js deleted file mode 100644 index 76266202..00000000 --- a/docs/assets/js/f4424813.c1da1dc0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[6628],{876:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>h});var a=n(2784);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},d=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),c=p(n),h=i,v=c["".concat(s,".").concat(h)]||c[h]||u[h]||r;return n?a.createElement(v,o(o({ref:t},d),{},{components:n})):a.createElement(v,o({ref:t},d))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p<r;p++)o[p]=n[p];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}c.displayName="MDXCreateElement"},3784:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>u,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(7896),i=(n(2784),n(876));const r={sidebar_position:3},o="LiveViewSocket API - Push",l={unversionedId:"liveview-socket/liveviewsocket-api-push",id:"liveview-socket/liveviewsocket-api-push",title:"LiveViewSocket API - Push",description:'There are various methods for "pushing" data from the server to the client outside of render and updating the URL of',source:"@site/docs/04-liveview-socket/liveviewsocket-api-push.md",sourceDirName:"04-liveview-socket",slug:"/liveview-socket/liveviewsocket-api-push",permalink:"/docs/liveview-socket/liveviewsocket-api-push",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"LiveViewSocket API - Context",permalink:"/docs/liveview-socket/liveviewsocket-api-context"},next:{title:"LiveViewSocket API - Server Events",permalink:"/docs/liveview-socket/liveviewsocket-api-infos"}},s={},p=[{value:"LiveViewSocket Properties and Methods",id:"liveviewsocket-properties-and-methods",level:2},{value:"Pushing Events",id:"pushing-events",level:2},{value:"Listening for Events on the Client",id:"listening-for-events-on-the-client",level:3},{value:"URL-based Pushes",id:"url-based-pushes",level:2},{value:"<code>pushPatch</code> Example",id:"pushpatch-example",level:3},{value:"<code>pushRedirect</code> Example",id:"pushredirect-example",level:3}],d={toc:p};function u(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveviewsocket-api---push"},"LiveViewSocket API - Push"),(0,i.kt)("p",null,'There are various methods for "pushing" data from the server to the client outside of ',(0,i.kt)("inlineCode",{parentName:"p"},"render")," and updating the URL of\nthe LiveView."),(0,i.kt)("h2",{id:"liveviewsocket-properties-and-methods"},"LiveViewSocket Properties and Methods"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Name"),(0,i.kt)("th",{parentName:"tr",align:null},"Description"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pushEvent(pushEvent: AnyLivePushEvent): void;")),(0,i.kt)("td",{parentName:"tr",align:null},"Pushes and event (possibly with data) from the server to the client. Requires either a ",(0,i.kt)("inlineCode",{parentName:"td"},"window.addEventListener")," defined for that event or a client ",(0,i.kt)("inlineCode",{parentName:"td"},"Hook")," to be defined and to be listening for the event via ",(0,i.kt)("inlineCode",{parentName:"td"},"this.handleEvent")," callback.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pushPatch(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Updates the LiveView's browser URL with the given path and query parameters.")),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"pushRedirect(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;")),(0,i.kt)("td",{parentName:"tr",align:null},"Shutdowns the current LiveView and loads another LiveView in its place without reloading the whole page (i.e., making a full HTTP request). Can be used to remount the current LiveView if need be. Use ",(0,i.kt)("inlineCode",{parentName:"td"},"pushPatch")," to update the current LiveView without unloading and remounting.")))),(0,i.kt)("h2",{id:"pushing-events"},"Pushing Events"),(0,i.kt)("p",null,"An event is any JSON object with a ",(0,i.kt)("inlineCode",{parentName:"p"},"type: string")," property and optionally any other key/value pairs. e.g., :"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'{\n type: "my-event",\n foo: "bar"\n}\n')),(0,i.kt)("p",null,"Events can be pushed to the client using ",(0,i.kt)("inlineCode",{parentName:"p"},"socket.pushEvent"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'socket.pushEvent({\n type: "my-event",\n foo: "bar",\n});\n')),(0,i.kt)("admonition",{type:"note"},(0,i.kt)("p",{parentName:"admonition"},"Event names are prefixed with ",(0,i.kt)("inlineCode",{parentName:"p"},"phx:")," so an event with the type ",(0,i.kt)("inlineCode",{parentName:"p"},"my-event")," will be sent as ",(0,i.kt)("inlineCode",{parentName:"p"},"phx:my-event"),".")),(0,i.kt)("h3",{id:"listening-for-events-on-the-client"},"Listening for Events on the Client"),(0,i.kt)("p",null,"Events can be listened for in two ways:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Using ",(0,i.kt)("inlineCode",{parentName:"li"},"window.addEventListener"),":")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'window.addEventListener("phx:my-event", (event) => {\n console.log(event.detail.foo); // "bar"\n});\n')),(0,i.kt)("ol",{start:2},(0,i.kt)("li",{parentName:"ol"},"Using a client ",(0,i.kt)("inlineCode",{parentName:"li"},"Hook"),":")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'this.handleEvent("my-event", (event) => {\n console.log(event.foo); // "bar"\n});\n')),(0,i.kt)("admonition",{title:"We haven't discussed Hooks yet but they are not in any way like a React Hook. They are a way to define a",type:"info"},(0,i.kt)("p",{parentName:"admonition"},"client-side component that can be used in your LiveView. We'll cover them in more detail in a later section."),(0,i.kt)("p",{parentName:"admonition"},"For now, just know that you can define a client-side component that can listen for events and do something with them.")),(0,i.kt)("h2",{id:"url-based-pushes"},"URL-based Pushes"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewSocket")," has two methods for updating the URL of the LiveView:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pushPatch")," - Updates the LiveView's browser URL with the given path and query parameters."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"pushRedirect")," - Shutdowns the current LiveView and loads another LiveView in its place without reloading the whole\npage (i.e., making a full HTTP request). Can be used to remount the current LiveView if need be.")),(0,i.kt)("h3",{id:"pushpatch-example"},(0,i.kt)("inlineCode",{parentName:"h3"},"pushPatch")," Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// Update the URL to /foo?bar=baz\nsocket.pushPatch("/foo", new URLSearchParams({ bar: "baz" }));\n')),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"pushPatch")," will cause the ",(0,i.kt)("inlineCode",{parentName:"p"},"handleParams")," method to be invoked which can be used to update the LiveView's state based on\nthe new URL parameters."),(0,i.kt)("h3",{id:"pushredirect-example"},(0,i.kt)("inlineCode",{parentName:"h3"},"pushRedirect")," Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'// Shutdown the current LiveView and load a new LiveView at /foo?bar=baz\nsocket.pushRedirect("/foo", new URLSearchParams({ bar: "baz" }));\n')),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"pushRedirect")," will cause the current LiveView to be shutdown and a new LiveView to be loaded at the given path and\nquery parameters ",(0,i.kt)("strong",{parentName:"p"},"without reloading the whole page")," (i.e., making a full HTTP request)."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/f4f34a3a.1d3ce6eb.js b/docs/assets/js/f4f34a3a.1d3ce6eb.js deleted file mode 100644 index 70f06a48..00000000 --- a/docs/assets/js/f4f34a3a.1d3ce6eb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[8636],{876:(e,t,r)=>{r.d(t,{Zo:()=>i,kt:()=>f});var o=r(2784);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function l(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?a(Object(r),!0).forEach((function(t){n(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):a(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,o,n=function(e,t){if(null==e)return{};var r,o,n={},a=Object.keys(e);for(o=0;o<a.length;o++)r=a[o],t.indexOf(r)>=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o<a.length;o++)r=a[o],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var c=o.createContext({}),u=function(e){var t=o.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},i=function(e){var t=u(e.components);return o.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,a=e.originalType,c=e.parentName,i=s(e,["components","mdxType","originalType","parentName"]),m=u(r),f=n,d=m["".concat(c,".").concat(f)]||m[f]||p[f]||a;return r?o.createElement(d,l(l({ref:t},i),{},{components:r})):o.createElement(d,l({ref:t},i))}));function f(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var a=r.length,l=new Array(a);l[0]=m;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:n,l[1]=s;for(var u=2;u<a;u++)l[u]=r[u];return o.createElement.apply(null,l)}return o.createElement.apply(null,r)}m.displayName="MDXCreateElement"},3137:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>u});var o=r(7896),n=(r(2784),r(876));const a={slug:"mdx-blog-post",title:"MDX Blog Post",authors:["floodfx"],tags:["docusaurus"]},l=void 0,s={permalink:"/blog/mdx-blog-post",source:"@site/blog/2021-08-01-mdx-blog-post.mdx",title:"MDX Blog Post",description:"Blog posts support Docusaurus Markdown features, such as MDX.",date:"2021-08-01T00:00:00.000Z",formattedDate:"August 1, 2021",tags:[{label:"docusaurus",permalink:"/blog/tags/docusaurus"}],readingTime:.175,hasTruncateMarker:!1,authors:[{name:"Donnie Flood",title:"LiveViewJS Author",url:"https://github.com/floodfx",imageURL:"https://github.com/floodfx.png",key:"floodfx"}],frontMatter:{slug:"mdx-blog-post",title:"MDX Blog Post",authors:["floodfx"],tags:["docusaurus"]},prevItem:{title:"Welcome",permalink:"/blog/welcome"},nextItem:{title:"Long Blog Post",permalink:"/blog/long-blog-post"}},c={authorsImageUrls:[void 0]},u=[],i={toc:u};function p(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},i,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"Blog posts support ",(0,n.kt)("a",{parentName:"p",href:"https://docusaurus.io/docs/markdown-features"},"Docusaurus Markdown features"),", such as ",(0,n.kt)("a",{parentName:"p",href:"https://mdxjs.com/"},"MDX"),"."),(0,n.kt)("admonition",{type:"tip"},(0,n.kt)("p",{parentName:"admonition"},"Use the power of React to create interactive blog posts."),(0,n.kt)("pre",{parentName:"admonition"},(0,n.kt)("code",{parentName:"pre",className:"language-js"},'<button onClick={() => alert("button clicked!")}>Click me!</button>\n')),(0,n.kt)("button",{onClick:()=>alert("button clicked!")},"Click me!")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/f52ab4ca.2a9123be.js b/docs/assets/js/f52ab4ca.2a9123be.js deleted file mode 100644 index be807a92..00000000 --- a/docs/assets/js/f52ab4ca.2a9123be.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[3776],{1778:e=>{e.exports=JSON.parse('{"title":"Quick Starts","description":"Run the examples and build your first LiveView","slug":"/category/quick-starts","permalink":"/docs/category/quick-starts","navigation":{"previous":{"title":"Gratitude","permalink":"/docs/overview/gratitude"},"next":{"title":"Download the Repo","permalink":"/docs/quick-starts/get-liveviewjs-repo"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/f98bd2c4.65b4ee08.js b/docs/assets/js/f98bd2c4.65b4ee08.js deleted file mode 100644 index 74c203d5..00000000 --- a/docs/assets/js/f98bd2c4.65b4ee08.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[2672],{9159:e=>{e.exports=JSON.parse('{"title":"Miscellaneous","description":"A potpourri of topics that don\'t fit elsewhere or are not large enough to warrant their own category","slug":"/category/miscellaneous","permalink":"/docs/category/miscellaneous","navigation":{"previous":{"title":"Support Webserver \\"X\\"","permalink":"/docs/webserver-integration/support-webserver-x"},"next":{"title":"Security Topics","permalink":"/docs/misc/security-topics"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/fb8b1f32.6f166156.js b/docs/assets/js/fb8b1f32.6f166156.js deleted file mode 100644 index 29d96232..00000000 --- a/docs/assets/js/fb8b1f32.6f166156.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[5149],{8433:e=>{e.exports=JSON.parse('{"title":"JS Commands","description":"Additional utilities for more dynamic, client-side experiences","slug":"/category/js-commands","permalink":"/docs/category/js-commands","navigation":{"previous":{"title":"Example Hook","permalink":"/docs/client-javascript/example-hook"},"next":{"title":"Overview","permalink":"/docs/js-commands/overview"}}}')}}]); \ No newline at end of file diff --git a/docs/assets/js/fe64d624.2d20b345.js b/docs/assets/js/fe64d624.2d20b345.js deleted file mode 100644 index 4e7e538b..00000000 --- a/docs/assets/js/fe64d624.2d20b345.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[4919],{876:(e,t,r)=>{r.d(t,{Zo:()=>d,kt:()=>w});var n=r(2784);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){i(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){if(null==e)return{};var r,n,i=function(e,t){if(null==e)return{};var r,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},d=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},v=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),v=p(r),w=i,u=v["".concat(l,".").concat(w)]||v[w]||c[w]||o;return r?n.createElement(u,a(a({ref:t},d),{},{components:r})):n.createElement(u,a({ref:t},d))}));function w(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=r.length,a=new Array(o);a[0]=v;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var p=2;p<o;p++)a[p]=r[p];return n.createElement.apply(null,a)}return n.createElement.apply(null,r)}v.displayName="MDXCreateElement"},3451:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var n=r(7896),i=(r(2784),r(876));const o={sidebar_position:3},a="LiveViewServerAdaptor",s={unversionedId:"webserver-integration/liveview-server-adaptor",id:"webserver-integration/liveview-server-adaptor",title:"LiveViewServerAdaptor",description:"LiveViewJS provides an interface that you can implement to integrate with your webserver of choice. This interface is",source:"@site/docs/12-webserver-integration/liveview-server-adaptor.md",sourceDirName:"12-webserver-integration",slug:"/webserver-integration/liveview-server-adaptor",permalink:"/docs/webserver-integration/liveview-server-adaptor",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"Webserver Integration",permalink:"/docs/webserver-integration/overview"},next:{title:'Support Webserver "X"',permalink:"/docs/webserver-integration/support-webserver-x"}},l={},p=[{value:"Required Methods",id:"required-methods",level:2},{value:"Digging into NodeExpressLiveViewServer",id:"digging-into-nodeexpressliveviewserver",level:2},{value:"HTTP Middleware",id:"http-middleware",level:3},{value:"Websocket Middleware",id:"websocket-middleware",level:3}],d={toc:p};function c(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"liveviewserveradaptor"},"LiveViewServerAdaptor"),(0,i.kt)("p",null,"LiveViewJS provides an interface that you can implement to integrate with your webserver of choice. This interface is\ncalled ",(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewServerAdaptor"),". This is the interface that is implemented by the ",(0,i.kt)("inlineCode",{parentName:"p"},"NodeExpressLiveViewServer")," (and\n",(0,i.kt)("inlineCode",{parentName:"p"},"DenoOakLiveViewServer"),")."),(0,i.kt)("h2",{id:"required-methods"},"Required Methods"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"LiveViewServerAdaptor")," requires that you implement the following methods:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"httpMiddleware()")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wsMiddleware()"))),(0,i.kt)("h2",{id:"digging-into-nodeexpressliveviewserver"},"Digging into NodeExpressLiveViewServer"),(0,i.kt)("p",null,"The implementation behind the ",(0,i.kt)("inlineCode",{parentName:"p"},"NodeExpressLiveViewServer")," is where the magic of mapping HTTP and websocket requests to\nLiveViewJS routes happens."),(0,i.kt)("h3",{id:"http-middleware"},"HTTP Middleware"),(0,i.kt)("p",null,"Let's look at the ExpressJS implementation of the ",(0,i.kt)("inlineCode",{parentName:"p"},"httpMiddleware")," method:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"httpMiddleware(): RequestHandler {\n return async (req: Request, res: Response, next: NextFunction) => {\n try {\n const adaptor = new ExpressRequestAdaptor(req, res, this.serDe);\n const { getRequestPath } = adaptor;\n\n // look up LiveView for route\n const liveview = this.router[getRequestPath()];\n if (!liveview) {\n // no LiveView found for route so call next() to\n // let a possible downstream route handle the request\n next();\n return;\n }\n\n // defer to liveviewjs to handle the request\n const rootViewHtml = await handleHttpLiveView(\n nanoid,\n nanoid,\n liveview,\n adaptor,\n this.htmlPageTemplate,\n this.liveTitleOptions,\n this.wrapperTemplate\n );\n\n // check if LiveView calls for a redirect and if so, do it\n if (adaptor.redirect) {\n res.redirect(adaptor.redirect);\n return;\n }\n\n // otherwise render the LiveView HTML\n res.format({\n html: () => {\n res.send(rootViewHtml);\n },\n });\n } catch (error) {\n next(error);\n }\n };\n}\n")),(0,i.kt)("p",null,"In summary, the ",(0,i.kt)("inlineCode",{parentName:"p"},"httpMiddleware")," method does the following:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"It creates an ",(0,i.kt)("inlineCode",{parentName:"li"},"ExpressRequestAdaptor")," instance that wraps the ExpressJS ",(0,i.kt)("inlineCode",{parentName:"li"},"Request")," and ",(0,i.kt)("inlineCode",{parentName:"li"},"Response")," objects so they can\nbe used by LiveViewJS."),(0,i.kt)("li",{parentName:"ol"},"It uses the LiveViewRouter to see if there is a LiveView registered for the request path."),(0,i.kt)("li",{parentName:"ol"},"If there is a LiveView registered for the request path, it calls ",(0,i.kt)("inlineCode",{parentName:"li"},"handleHttpLiveView")," to handle the request.\n",(0,i.kt)("inlineCode",{parentName:"li"},"handleHttpLiveView")," is provided by LiveViewJS that connect the request to the LiveView."),(0,i.kt)("li",{parentName:"ol"},"If there is no LiveView registered for the request path, it calls ",(0,i.kt)("inlineCode",{parentName:"li"},"next()")," to let the next middleware in the chain\nhandle the request."),(0,i.kt)("li",{parentName:"ol"},"We check for redirects and if there is one, we do it."),(0,i.kt)("li",{parentName:"ol"},"Otherwise, we render the LiveView HTML.")),(0,i.kt)("h3",{id:"websocket-middleware"},"Websocket Middleware"),(0,i.kt)("p",null,"Let's look at the ExpressJS implementation of the ",(0,i.kt)("inlineCode",{parentName:"p"},"wsMiddleware")," method:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'wsMiddleware(): (wsServer: WebSocketServer) => Promise<void> {\n return async (wsServer: WebSocketServer) => {\n // send websocket requests to the LiveViewJS message router\n wsServer.on("connection", (ws) => {\n const connectionId = nanoid();\n ws.on("message", async (message, isBinary) => {\n // pass websocket messages to LiveViewJS\n await this._wsRouter.onMessage(connectionId, message, new NodeWsAdaptor(ws), isBinary);\n });\n ws.on("close", async () => {\n // pass websocket close events to LiveViewJS\n await this._wsRouter.onClose(connectionId);\n });\n });\n };\n}\n')),(0,i.kt)("p",null,"In summary, the ",(0,i.kt)("inlineCode",{parentName:"p"},"wsMiddleware")," method listens for websocket connections, messages, and close events and passes them to\nthe LiveViewJS message router. The ",(0,i.kt)("inlineCode",{parentName:"p"},"wsRouter")," knows how to route websocket messages to the correct LiveView and handle\nthe websocket lifecycle. Not much to see here since it's all handled by LiveViewJS."),(0,i.kt)("p",null,"That's more or less it modulo the ",(0,i.kt)("inlineCode",{parentName:"p"},"ExpressRequestAdaptor"),". The ",(0,i.kt)("inlineCode",{parentName:"p"},"ExpressRequestAdaptor")," is a wrapper around the ExpressJS\n",(0,i.kt)("inlineCode",{parentName:"p"},"Request")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"Response")," objects that provides a common interface for LiveViewJS to use. This is another necessary step\nto normalize any differences between webserver implementations."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/docs/assets/js/main.bb008988.js b/docs/assets/js/main.bb008988.js deleted file mode 100644 index e3dea9c3..00000000 --- a/docs/assets/js/main.bb008988.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! For license information please see main.bb008988.js.LICENSE.txt */ -(self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[]).push([[179],{9895:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(2784),a=n(7896),o=n(9028),i=n.n(o),l=n(6887);const s={"00bb4f2e":[()=>n.e(9563).then(n.bind(n,8483)),"@site/docs/03-anatomy-of-a-liveview/render-details.md",8483],"01a85c17":[()=>Promise.all([n.e(532),n.e(4013)]).then(n.bind(n,3270)),"@theme/BlogTagsListPage",3270],"031793e1":[()=>n.e(1633).then(n.t.bind(n,2511,19)),"~blog/default/blog-tags-facebook-038.json",2511],"04151870":[()=>n.e(6847).then(n.bind(n,6847)),"@site/docs/06-user-events-slash-bindings/overview.md",6847],"05a1a0e3":[()=>n.e(8379).then(n.bind(n,3209)),"@site/docs/04-liveview-socket/liveviewsocket-api-misc.md",3209],"05efe52d":[()=>n.e(1026).then(n.bind(n,329)),"@site/docs/13-misc/statics-and-dynamics.md",329],"06e5e56e":[()=>n.e(5727).then(n.bind(n,8353)),"@site/docs/03-anatomy-of-a-liveview/handle-info.md",8353],"0832cd5e":[()=>n.e(5742).then(n.bind(n,9222)),"@site/docs/11-js-commands/transition-cmd.md",9222],"096bfee4":[()=>n.e(7178).then(n.t.bind(n,5010,19)),"~blog/default/blog-tags-facebook-038-list.json",5010],"0ad92f1a":[()=>n.e(4221).then(n.bind(n,6609)),"@site/docs/02-quick-starts/nodejs-build-first-liveview.md",6609],"0bd32f56":[()=>n.e(8030).then(n.bind(n,3180)),"@site/docs/13-misc/flash.md",3180],"0e8a1ffd":[()=>n.e(3304).then(n.bind(n,2667)),"@site/docs/04-liveview-socket/liveviewsocket-api-context.md",2667],"11e44ae3":[()=>n.e(6739).then(n.bind(n,9456)),"@site/docs/01-overview/gratitude.md",9456],"12d121b1":[()=>n.e(9133).then(n.bind(n,1853)),"@site/docs/13-misc/debugging-wire.md",1853],"14eb3368":[()=>Promise.all([n.e(532),n.e(9817)]).then(n.bind(n,6544)),"@theme/DocCategoryGeneratedIndexPage",6544],"15989f81":[()=>n.e(610).then(n.bind(n,7522)),"@site/docs/03-anatomy-of-a-liveview/handle-event-details.md",7522],17896441:[()=>Promise.all([n.e(532),n.e(5674),n.e(7918)]).then(n.bind(n,4726)),"@theme/DocItem",4726],"1be78505":[()=>Promise.all([n.e(532),n.e(9514)]).then(n.bind(n,4959)),"@theme/DocPage",4959],"1df93b7f":[()=>Promise.all([n.e(532),n.e(3237)]).then(n.bind(n,3593)),"@site/src/pages/index.tsx",3593],"1fdf7751":[()=>n.e(7800).then(n.bind(n,633)),"@site/docs/01-overview/runtimes.md",633],"2043d2f8":[()=>n.e(5405).then(n.t.bind(n,1065,19)),"~docs/default/category-docs-tutorialsidebar-category-client-side-javascript-7f5.json",1065],"218c334f":[()=>n.e(7203).then(n.bind(n,8138)),"@site/docs/03-anatomy-of-a-liveview/handle-info-pub-sub.md",8138],"26de17c9":[()=>n.e(3476).then(n.bind(n,6512)),"@site/docs/03-anatomy-of-a-liveview/handle-info-user-initiated.md",6512],"284ba78b":[()=>n.e(1544).then(n.bind(n,5265)),"@site/docs/03-anatomy-of-a-liveview/handle-info-background-task.md",5265],29143544:[()=>n.e(6250).then(n.t.bind(n,4749,19)),"~docs/default/category-docs-tutorialsidebar-category-uploading-files-fe8.json",4749],"2c6e467f":[()=>n.e(6304).then(n.t.bind(n,8530,19)),"~docs/default/category-docs-tutorialsidebar-category-real-time-multi-player-d8b.json",8530],"2f79913e":[()=>n.e(728).then(n.t.bind(n,1432,19)),"~docs/default/category-docs-tutorialsidebar-category-webserver-integrations-772.json",1432],"3081b21a":[()=>n.e(9944).then(n.bind(n,1067)),"@site/docs/08-file-upload/upload-config-options.md",1067],"30a24c52":[()=>n.e(453).then(n.t.bind(n,8605,19)),"~blog/default/blog-tags-hello-039.json",8605],"33cd0f01":[()=>n.e(458).then(n.bind(n,3254)),"@site/docs/02-quick-starts/nodejs-run-examples.md",3254],"355cffd0":[()=>n.e(3380).then(n.bind(n,1200)),"@site/docs/07-forms-and-changesets/changesets.md",1200],"3de70efe":[()=>n.e(1247).then(n.bind(n,9962)),"@site/docs/02-quick-starts/get-liveviewjs-repo.md",9962],"3fc56706":[()=>n.e(4085).then(n.bind(n,1080)),"@site/docs/13-misc/root-and-page-renderers.md",1080],42132283:[()=>n.e(3369).then(n.bind(n,8281)),"@site/docs/10-client-javascript/overview.md",8281],"42d1d491":[()=>n.e(3219).then(n.t.bind(n,4626,19)),"~docs/default/category-docs-tutorialsidebar-category-anatomy-of-a-liveview-310.json",4626],"4c9e35b1":[()=>n.e(9035).then(n.t.bind(n,499,19)),"~blog/default/blog-tags-hola-ea2-list.json",499],"4ec89f2d":[()=>n.e(4819).then(n.bind(n,3540)),"@site/docs/11-js-commands/dispatch-cmd.md",3540],"4f66b655":[()=>n.e(3919).then(n.bind(n,9764)),"@site/docs/08-file-upload/image-preview.md",9764],59362658:[()=>n.e(2267).then(n.bind(n,1987)),"@site/blog/2021-08-01-mdx-blog-post.mdx",1987],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,6809)),"@generated/docusaurus.config",6809],"5f30c8bb":[()=>n.e(8259).then(n.bind(n,3831)),"@site/docs/02-quick-starts/deno-run-examples.md",3831],"608ae6a4":[()=>n.e(6938).then(n.t.bind(n,4545,19)),"~blog/default/blog-tags-docusaurus-0e0-list.json",4545],"630c0ad8":[()=>n.e(7911).then(n.bind(n,1217)),"@site/docs/05-lifecycle-of-a-liveview/intro.md",1217],"64a3b801":[()=>n.e(1894).then(n.t.bind(n,5745,19)),"/Users/nicholasduffy/code/liveviewjs/apps/liveviewjs.com/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",5745],66406991:[()=>n.e(110).then(n.t.bind(n,711,19)),"~blog/default/blog-tags-hello-039-list.json",711],"66ff5741":[()=>n.e(5539).then(n.bind(n,6075)),"@site/docs/07-forms-and-changesets/use-with-forms.md",6075],"6875c492":[()=>Promise.all([n.e(532),n.e(5674),n.e(6468),n.e(8610)]).then(n.bind(n,242)),"@theme/BlogTagsPostsPage",242],"6bf4f685":[()=>n.e(2294).then(n.bind(n,7709)),"@site/docs/09-real-time-multi-player-pub-sub/overview.md",7709],"6c9f2a26":[()=>n.e(5071).then(n.bind(n,471)),"@site/docs/11-js-commands/overview.md",471],"73664a40":[()=>n.e(3514).then(n.bind(n,1717)),"@site/blog/2019-05-29-long-blog-post.md",1717],"7661071f":[()=>n.e(9642).then(n.bind(n,2843)),"@site/blog/2021-08-26-welcome/index.md?truncated=true",2843],76864381:[()=>n.e(2493).then(n.bind(n,4776)),"@site/docs/10-client-javascript/example-hook.md",4776],"7834bba6":[()=>n.e(5967).then(n.bind(n,5074)),"@site/docs/11-js-commands/push-cmd.md",5074],"78c55e7e":[()=>n.e(6561).then(n.t.bind(n,8416,19)),"~docs/default/category-docs-tutorialsidebar-category-user-events-079.json",8416],"7d980dc2":[()=>n.e(9376).then(n.bind(n,8062)),"@site/docs/04-liveview-socket/liveviewsocket-api.md",8062],"814f3328":[()=>n.e(2535).then(n.t.bind(n,5641,19)),"~blog/default/blog-post-list-prop-default.json",5641],"8717b14a":[()=>n.e(948).then(n.bind(n,457)),"@site/blog/2019-05-29-long-blog-post.md?truncated=true",457],"88861ccc":[()=>n.e(645).then(n.bind(n,1677)),"@site/docs/01-overview/feedback.md",1677],"88b645ee":[()=>n.e(8168).then(n.t.bind(n,1536,19)),"~docs/default/category-docs-tutorialsidebar-category-overview-bc5.json",1536],"8908bd66":[()=>n.e(6527).then(n.bind(n,5289)),"@site/docs/06-user-events-slash-bindings/rate-limiting-bindings.md",5289],"925b3f96":[()=>n.e(9003).then(n.bind(n,6954)),"@site/blog/2019-05-28-first-blog-post.md?truncated=true",6954],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"9578e4ed":[()=>n.e(8686).then(n.t.bind(n,3928,19)),"~docs/default/category-docs-tutorialsidebar-category-lifecycle-of-a-liveview-c39.json",3928],"9a32815e":[()=>n.e(5548).then(n.bind(n,5915)),"@site/docs/02-quick-starts/deno-build-first-liveview.md",5915],"9b689780":[()=>n.e(5723).then(n.bind(n,2738)),"@site/docs/04-liveview-socket/liveviewsocket-api-infos.md",2738],"9bbe50cd":[()=>n.e(9080).then(n.bind(n,5797)),"@site/docs/03-anatomy-of-a-liveview/mount-details.md",5797],"9c3a86b6":[()=>n.e(1440).then(n.bind(n,4640)),"@site/docs/12-webserver-integration/overview.md",4640],"9e4087bc":[()=>n.e(3608).then(n.bind(n,3008)),"@theme/BlogArchivePage",3008],a49e487e:[()=>n.e(1037).then(n.bind(n,7797)),"@site/docs/13-misc/security-topics.md",7797],a6aa9e1f:[()=>Promise.all([n.e(532),n.e(5674),n.e(6468),n.e(3089)]).then(n.bind(n,9956)),"@theme/BlogListPage",9956],a7023ddc:[()=>n.e(1713).then(n.t.bind(n,3457,19)),"~blog/default/blog-tags-tags-4c2.json",3457],a7e77012:[()=>n.e(450).then(n.bind(n,9409)),"@site/docs/04-liveview-socket/raw-liveviewsocket-api.md",9409],a80da1cf:[()=>n.e(3205).then(n.t.bind(n,4863,19)),"~blog/default/blog-tags-docusaurus-0e0.json",4863],ae0c09be:[()=>n.e(5024).then(n.bind(n,5351)),"@site/docs/01-overview/introduction.md",5351],b2b675dd:[()=>n.e(533).then(n.t.bind(n,8017,19)),"~blog/default/blog-c06.json",8017],b2f554cd:[()=>n.e(1477).then(n.t.bind(n,10,19)),"~blog/default/blog-archive-80c.json",10],b747b5f5:[()=>n.e(7599).then(n.t.bind(n,3500,19)),"~docs/default/category-docs-tutorialsidebar-category-forms-changesets-323.json",3500],b7a0085b:[()=>n.e(6258).then(n.bind(n,3132)),"@site/docs/08-file-upload/drag-and-drop.md",3132],bac9275f:[()=>n.e(4161).then(n.t.bind(n,4469,19)),"/Users/nicholasduffy/code/liveviewjs/apps/liveviewjs.com/.docusaurus/docusaurus-plugin-content-blog/default/plugin-route-context-module-100.json",4469],bc63dc68:[()=>n.e(9119).then(n.bind(n,1905)),"@site/docs/11-js-commands/add-remove-class.md",1905],be227171:[()=>n.e(6300).then(n.bind(n,296)),"@site/docs/03-anatomy-of-a-liveview/handle-params.md",296],c1a16298:[()=>n.e(6119).then(n.bind(n,1777)),"@site/docs/07-forms-and-changesets/overview.md",1777],c68b9697:[()=>n.e(914).then(n.t.bind(n,6962,19)),"~docs/default/category-docs-tutorialsidebar-category-liveviewsocket-c95.json",6962],ccc49370:[()=>Promise.all([n.e(532),n.e(5674),n.e(6468),n.e(6103)]).then(n.bind(n,1651)),"@theme/BlogPostPage",1651],ccf3ea07:[()=>n.e(7875).then(n.bind(n,7517)),"@site/docs/06-user-events-slash-bindings/additional-bindings.md",7517],d4048a91:[()=>n.e(1523).then(n.bind(n,4317)),"@site/docs/06-user-events-slash-bindings/bindings-table.md",4317],d5bbcc50:[()=>n.e(9843).then(n.bind(n,2625)),"@site/docs/09-real-time-multi-player-pub-sub/example-pub-sub.md",2625],d63d7ef8:[()=>n.e(8685).then(n.bind(n,14)),"@site/docs/12-webserver-integration/support-webserver-x.md",14],d6e2e8d0:[()=>n.e(6242).then(n.bind(n,3779)),"@site/docs/11-js-commands/set-remove-attr.md",3779],d8b15fc0:[()=>n.e(1087).then(n.bind(n,7850)),"@site/docs/01-overview/paradigm.md",7850],d9f32620:[()=>n.e(1914).then(n.bind(n,8446)),"@site/blog/2021-08-26-welcome/index.md",8446],dddb404b:[()=>n.e(6011).then(n.bind(n,6090)),"@site/docs/11-js-commands/show-hide-toggle-el.md",6090],e16015ca:[()=>n.e(9700).then(n.t.bind(n,5688,19)),"~blog/default/blog-tags-hola-ea2.json",5688],e273c56f:[()=>n.e(2362).then(n.bind(n,4211)),"@site/blog/2019-05-28-first-blog-post.md",4211],e53ecdb2:[()=>n.e(9484).then(n.bind(n,2964)),"@site/docs/03-anatomy-of-a-liveview/liveview-api.md",2964],e96d4430:[()=>n.e(4379).then(n.bind(n,9647)),"@site/docs/08-file-upload/overview.md",9647],eb3e9177:[()=>n.e(653).then(n.bind(n,5119)),"@site/docs/10-client-javascript/client-hooks.md",5119],ebc9356c:[()=>n.e(9991).then(n.t.bind(n,3769,19)),"/Users/nicholasduffy/code/liveviewjs/apps/liveviewjs.com/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",3769],ed398bd8:[()=>n.e(9773).then(n.bind(n,3801)),"@site/docs/04-liveview-socket/liveviewsocket-api-uploads.md",3801],f08e994e:[()=>n.e(333).then(n.bind(n,4969)),"@site/docs/13-misc/livecomponents.md",4969],f4424813:[()=>n.e(6628).then(n.bind(n,3784)),"@site/docs/04-liveview-socket/liveviewsocket-api-push.md",3784],f4f34a3a:[()=>n.e(8636).then(n.bind(n,3137)),"@site/blog/2021-08-01-mdx-blog-post.mdx?truncated=true",3137],f52ab4ca:[()=>n.e(3776).then(n.t.bind(n,1778,19)),"~docs/default/category-docs-tutorialsidebar-category-quick-starts-afb.json",1778],f98bd2c4:[()=>n.e(2672).then(n.t.bind(n,9159,19)),"~docs/default/category-docs-tutorialsidebar-category-miscellaneous-ba3.json",9159],fb8b1f32:[()=>n.e(5149).then(n.t.bind(n,8433,19)),"~docs/default/category-docs-tutorialsidebar-category-js-commands-b99.json",8433],fe64d624:[()=>n.e(4919).then(n.bind(n,3451)),"@site/docs/12-webserver-integration/liveview-server-adaptor.md",3451]};function c(e){let{error:t,retry:n,pastDelay:a}=e;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var u=n(805),d=n(1313);function f(e,t){if("*"===e)return i()({loading:c,loader:()=>n.e(2006).then(n.bind(n,2006)),modules:["@theme/NotFound"],webpack:()=>[2006],render(e,t){const n=e.default;return r.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});const o=l[e+"-"+t],f={},p=[],m=[],h=(0,u.Z)(o);return Object.entries(h).forEach((e=>{let[t,n]=e;const r=s[n];r&&(f[t]=r[0],p.push(r[1]),m.push(r[2]))})),i().Map({loading:c,loader:f,modules:p,webpack:()=>m,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,r]=t;const a=r.default;if(!a)throw new Error("The page component at "+e+" doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.");"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{a[e]=r[e]}));let o=i;const l=n.split(".");l.slice(0,-1).forEach((e=>{o=o[e]})),o[l[l.length-1]]=a}));const l=i.__comp;delete i.__comp;const s=i.__context;return delete i.__context,r.createElement(d.z,{value:s},r.createElement(l,(0,a.Z)({},i,n)))}})}const p=[{path:"/blog",component:f("/blog","1d5"),exact:!0},{path:"/blog/archive",component:f("/blog/archive","809"),exact:!0},{path:"/blog/first-blog-post",component:f("/blog/first-blog-post","72f"),exact:!0},{path:"/blog/long-blog-post",component:f("/blog/long-blog-post","52a"),exact:!0},{path:"/blog/mdx-blog-post",component:f("/blog/mdx-blog-post","bf8"),exact:!0},{path:"/blog/tags",component:f("/blog/tags","8de"),exact:!0},{path:"/blog/tags/docusaurus",component:f("/blog/tags/docusaurus","138"),exact:!0},{path:"/blog/tags/facebook",component:f("/blog/tags/facebook","2d9"),exact:!0},{path:"/blog/tags/hello",component:f("/blog/tags/hello","193"),exact:!0},{path:"/blog/tags/hola",component:f("/blog/tags/hola","e44"),exact:!0},{path:"/blog/welcome",component:f("/blog/welcome","905"),exact:!0},{path:"/docs",component:f("/docs","652"),routes:[{path:"/docs/anatomy-of-a-liveview/handle-event-details",component:f("/docs/anatomy-of-a-liveview/handle-event-details","c2a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/handle-info",component:f("/docs/anatomy-of-a-liveview/handle-info","326"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/handle-info-background-task",component:f("/docs/anatomy-of-a-liveview/handle-info-background-task","645"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/handle-info-pub-sub",component:f("/docs/anatomy-of-a-liveview/handle-info-pub-sub","868"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/handle-info-user-initiated",component:f("/docs/anatomy-of-a-liveview/handle-info-user-initiated","14f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/handle-params",component:f("/docs/anatomy-of-a-liveview/handle-params","a7c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/liveview-api",component:f("/docs/anatomy-of-a-liveview/liveview-api","a61"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/mount-details",component:f("/docs/anatomy-of-a-liveview/mount-details","c2b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/anatomy-of-a-liveview/render-details",component:f("/docs/anatomy-of-a-liveview/render-details","892"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/anatomy-of-a-liveview",component:f("/docs/category/anatomy-of-a-liveview","5f6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/client-side-javascript",component:f("/docs/category/client-side-javascript","a40"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/forms--changesets",component:f("/docs/category/forms--changesets","364"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/js-commands",component:f("/docs/category/js-commands","71b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/lifecycle-of-a-liveview",component:f("/docs/category/lifecycle-of-a-liveview","060"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/liveviewsocket",component:f("/docs/category/liveviewsocket","dff"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/miscellaneous",component:f("/docs/category/miscellaneous","650"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/overview",component:f("/docs/category/overview","a36"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/quick-starts",component:f("/docs/category/quick-starts","60d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/real-time--multi-player",component:f("/docs/category/real-time--multi-player","093"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/uploading-files",component:f("/docs/category/uploading-files","e78"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/user-events",component:f("/docs/category/user-events","e0c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/category/webserver-integrations",component:f("/docs/category/webserver-integrations","481"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/client-javascript/client-hooks",component:f("/docs/client-javascript/client-hooks","3f6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/client-javascript/example-hook",component:f("/docs/client-javascript/example-hook","548"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/client-javascript/overview",component:f("/docs/client-javascript/overview","b84"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/file-upload/drag-and-drop",component:f("/docs/file-upload/drag-and-drop","fda"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/file-upload/image-preview",component:f("/docs/file-upload/image-preview","513"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/file-upload/overview",component:f("/docs/file-upload/overview","0e5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/file-upload/upload-config-options",component:f("/docs/file-upload/upload-config-options","4cd"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/forms-and-changesets/changesets",component:f("/docs/forms-and-changesets/changesets","178"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/forms-and-changesets/overview",component:f("/docs/forms-and-changesets/overview","20b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/forms-and-changesets/use-with-forms",component:f("/docs/forms-and-changesets/use-with-forms","958"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/add-remove-class",component:f("/docs/js-commands/add-remove-class","59b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/dispatch-cmd",component:f("/docs/js-commands/dispatch-cmd","ec4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/overview",component:f("/docs/js-commands/overview","5da"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/push-cmd",component:f("/docs/js-commands/push-cmd","12f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/set-remove-attr",component:f("/docs/js-commands/set-remove-attr","33c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/show-hide-toggle-el",component:f("/docs/js-commands/show-hide-toggle-el","3d3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/js-commands/transition-cmd",component:f("/docs/js-commands/transition-cmd","c60"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/lifecycle-of-a-liveview/intro",component:f("/docs/lifecycle-of-a-liveview/intro","263"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/liveviewsocket-api",component:f("/docs/liveview-socket/liveviewsocket-api","43e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/liveviewsocket-api-context",component:f("/docs/liveview-socket/liveviewsocket-api-context","003"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/liveviewsocket-api-infos",component:f("/docs/liveview-socket/liveviewsocket-api-infos","ece"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/liveviewsocket-api-misc",component:f("/docs/liveview-socket/liveviewsocket-api-misc","fda"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/liveviewsocket-api-push",component:f("/docs/liveview-socket/liveviewsocket-api-push","369"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/liveviewsocket-api-uploads",component:f("/docs/liveview-socket/liveviewsocket-api-uploads","a2d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/liveview-socket/raw-liveviewsocket-api",component:f("/docs/liveview-socket/raw-liveviewsocket-api","32f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/misc/debugging-wire",component:f("/docs/misc/debugging-wire","fcc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/misc/flash",component:f("/docs/misc/flash","aa2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/misc/livecomponents",component:f("/docs/misc/livecomponents","2bd"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/misc/root-and-page-renderers",component:f("/docs/misc/root-and-page-renderers","ead"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/misc/security-topics",component:f("/docs/misc/security-topics","2cb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/misc/statics-and-dynamics",component:f("/docs/misc/statics-and-dynamics","5bd"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/overview/feedback",component:f("/docs/overview/feedback","ccc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/overview/gratitude",component:f("/docs/overview/gratitude","384"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/overview/introduction",component:f("/docs/overview/introduction","7ac"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/overview/paradigm",component:f("/docs/overview/paradigm","a3a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/overview/runtimes",component:f("/docs/overview/runtimes","1ce"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/quick-starts/deno-build-first-liveview",component:f("/docs/quick-starts/deno-build-first-liveview","2c4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/quick-starts/deno-run-examples",component:f("/docs/quick-starts/deno-run-examples","6ba"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/quick-starts/get-liveviewjs-repo",component:f("/docs/quick-starts/get-liveviewjs-repo","9bf"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/quick-starts/nodejs-build-first-liveview",component:f("/docs/quick-starts/nodejs-build-first-liveview","d37"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/quick-starts/nodejs-run-examples",component:f("/docs/quick-starts/nodejs-run-examples","acf"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/real-time-multi-player-pub-sub/example-pub-sub",component:f("/docs/real-time-multi-player-pub-sub/example-pub-sub","839"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/real-time-multi-player-pub-sub/overview",component:f("/docs/real-time-multi-player-pub-sub/overview","00e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/user-events-slash-bindings/additional-bindings",component:f("/docs/user-events-slash-bindings/additional-bindings","4e4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/user-events-slash-bindings/bindings-table",component:f("/docs/user-events-slash-bindings/bindings-table","359"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/user-events-slash-bindings/overview",component:f("/docs/user-events-slash-bindings/overview","868"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/user-events-slash-bindings/rate-limiting-bindings",component:f("/docs/user-events-slash-bindings/rate-limiting-bindings","054"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/webserver-integration/liveview-server-adaptor",component:f("/docs/webserver-integration/liveview-server-adaptor","7f6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/webserver-integration/overview",component:f("/docs/webserver-integration/overview","e05"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/webserver-integration/support-webserver-x",component:f("/docs/webserver-integration/support-webserver-x","587"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"/",component:f("/","d5f"),exact:!0},{path:"*",component:f("*")}]},108:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(2784);const a=r.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{o(!0)}),[]),r.createElement(a.Provider,{value:n},t)}},6445:(e,t,n)=>{"use strict";var r=n(2784),a=n(8316),o=n(7933),i=n(2175),l=n(1263);const s=[n(879),n(5749),n(6120),n(401),n(1964)];var c=n(9895),u=n(3181),d=n(9702);function f(e){let{children:t}=e;return r.createElement(r.Fragment,null,t)}var p=n(7896),m=n(9854),h=n(7614),g=n(77),v=n(7683),b=n(328),y=n(5552),w=n(9499),k=n(4925),E=n(4390);function S(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,h.Z)(),n=(0,y.l)();return r.createElement(m.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:a}]=e;return r.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:a})})),r.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function x(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.Z)(),a=function(){const{siteConfig:{url:e}}=(0,h.Z)(),{pathname:t}=(0,u.TH)();return e+(0,g.Z)(t)}(),o=t?""+n+t:a;return r.createElement(m.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function _(){const{i18n:{currentLocale:e}}=(0,h.Z)(),{metadata:t,image:n}=(0,v.L)();return r.createElement(r.Fragment,null,r.createElement(m.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:w.h})),n&&r.createElement(b.d,{image:n}),r.createElement(x,null),r.createElement(S,null),r.createElement(E.Z,{tag:k.HX,locale:e}),r.createElement(m.Z,null,t.map(((e,t)=>r.createElement("meta",(0,p.Z)({key:t},e))))))}const C=new Map;function T(e){if(C.has(e.pathname))return{...e,pathname:C.get(e.pathname)};if((0,d.f)(c.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return C.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return C.set(e.pathname,t),{...e,pathname:t}}var L=n(108),A=n(8874);function P(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];const a=s.map((t=>{var r,a;const o=null!=(r=null==(a=t.default)?void 0:a[e])?r:t[e];return null==o?void 0:o(...n)}));return()=>a.forEach((e=>null==e?void 0:e()))}const R=function(e){let{children:t,location:n,previousLocation:a}=e;return(0,r.useLayoutEffect)((()=>{a!==n&&(a&&function(e){const{hash:t}=e;if(t){const e=decodeURIComponent(t.substring(1)),n=document.getElementById(e);null==n||n.scrollIntoView()}else window.scrollTo(0,0)}(n),P("onRouteDidUpdate",{previousLocation:a,location:n}))}),[a,n]),t};function N(e){const t=(0,d.f)(c.Z,e);return Promise.all(t.map((e=>null==e.route.component.preload?void 0:e.route.component.preload())))}class O extends r.Component{constructor(e){super(e),this.previousLocation=void 0,this.routeUpdateCleanupCb=void 0,this.previousLocation=null,this.routeUpdateCleanupCb=l.Z.canUseDOM?P("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=P("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),N(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return r.createElement(R,{previousLocation:this.previousLocation,location:t},r.createElement(u.AW,{location:t,render:()=>e}))}}const D=O,I="docusaurus-base-url-issue-banner-container",M="docusaurus-base-url-issue-banner-suggestion-container",j="__DOCUSAURUS_INSERT_BASEURL_BANNER";function F(e){return"\nwindow['"+j+"'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['"+j+"'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('"+I+"');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = "+JSON.stringify(function(e){return'\n<div id="docusaurus-base-url-issue-banner" style="border: thick solid red; background-color: rgb(255, 230, 179); margin: 20px; padding: 20px; font-size: 20px;">\n <p style="font-weight: bold; font-size: 30px;">Your Docusaurus site did not load properly.</p>\n <p>A very common reason is a wrong site <a href="https://docusaurus.io/docs/docusaurus.config.js/#baseurl" style="font-weight: bold;">baseUrl configuration</a>.</p>\n <p>Current configured baseUrl = <span style="font-weight: bold; color: red;">'+e+"</span> "+("/"===e?" (default value)":"")+'</p>\n <p>We suggest trying baseUrl = <span id="'+M+'" style="font-weight: bold; color: green;"></span></p>\n</div>\n'}(e)).replace(/</g,"\\<")+";\n bannerContainer.innerHTML = bannerHtml;\n var suggestionContainer = document.getElementById('"+M+"');\n var actualHomePagePath = window.location.pathname;\n var suggestedBaseUrl = actualHomePagePath.substr(-1) === '/'\n ? actualHomePagePath\n : actualHomePagePath + '/';\n suggestionContainer.innerHTML = suggestedBaseUrl;\n}\n"}function B(){const{siteConfig:{baseUrl:e}}=(0,h.Z)();return(0,r.useLayoutEffect)((()=>{window[j]=!1}),[]),r.createElement(r.Fragment,null,!l.Z.canUseDOM&&r.createElement(m.Z,null,r.createElement("script",null,F(e))),r.createElement("div",{id:I}))}function z(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,h.Z)(),{pathname:n}=(0,u.TH)();return t&&n===e?r.createElement(B,null):null}function U(){const{siteConfig:{favicon:e,title:t},i18n:{currentLocale:n,localeConfigs:a}}=(0,h.Z)(),o=(0,g.Z)(e),{htmlLang:i,direction:l}=a[n];return r.createElement(m.Z,null,r.createElement("html",{lang:i,dir:l}),r.createElement("title",null,t),r.createElement("meta",{property:"og:title",content:t}),e&&r.createElement("link",{rel:"icon",href:o}))}var q=n(3951);function G(){const e=(0,d.H)(c.Z),t=(0,u.TH)();return r.createElement(q.Z,null,r.createElement(A.M,null,r.createElement(L.t,null,r.createElement(f,null,r.createElement(U,null),r.createElement(_,null),r.createElement(z,null),r.createElement(D,{location:T(t)},e)))))}var $=n(6887);const H=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{var r,a;if("undefined"==typeof document)return void n();const o=document.createElement("link");o.setAttribute("rel","prefetch"),o.setAttribute("href",e),o.onload=()=>t(),o.onerror=()=>n();const i=null!=(r=document.getElementsByTagName("head")[0])?r:null==(a=document.getElementsByName("script")[0])?void 0:a.parentNode;null==i||i.appendChild(o)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var Z=n(805);const V=new Set,W=new Set,Y=()=>{var e,t;return(null==(e=navigator.connection)?void 0:e.effectiveType.includes("2g"))||(null==(t=navigator.connection)?void 0:t.saveData)},K={prefetch(e){if(!(e=>!Y()&&!W.has(e)&&!V.has(e))(e))return!1;V.add(e);const t=(0,d.f)(c.Z,e).flatMap((e=>{return t=e.route.path,Object.entries($).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Z.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?H(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!Y()&&!W.has(e))(e)&&(W.add(e),N(e))},Q=Object.freeze(K);if(l.Z.canUseDOM){window.docusaurus=Q;const e=a.hydrate;N(window.location.pathname).then((()=>{e(r.createElement(i.B6,null,r.createElement(o.VK,null,r.createElement(G,null))),document.getElementById("__docusaurus"))}))}},8874:(e,t,n)=>{"use strict";n.d(t,{_:()=>u,M:()=>d});var r=n(2784),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-google-gtag":{"default":{"trackingID":"G-WXB1PQ0VMD","anonymizeIP":true,"id":"default"}},"docusaurus-plugin-content-docs":{"default":{"path":"/docs","versions":[{"name":"current","label":"Next","isLast":true,"path":"/docs","mainDocId":"overview/introduction","docs":[{"id":"anatomy-of-a-liveview/handle-event-details","path":"/docs/anatomy-of-a-liveview/handle-event-details","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/handle-info","path":"/docs/anatomy-of-a-liveview/handle-info","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/handle-info-background-task","path":"/docs/anatomy-of-a-liveview/handle-info-background-task","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/handle-info-pub-sub","path":"/docs/anatomy-of-a-liveview/handle-info-pub-sub","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/handle-info-user-initiated","path":"/docs/anatomy-of-a-liveview/handle-info-user-initiated","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/handle-params","path":"/docs/anatomy-of-a-liveview/handle-params","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/liveview-api","path":"/docs/anatomy-of-a-liveview/liveview-api","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/mount-details","path":"/docs/anatomy-of-a-liveview/mount-details","sidebar":"tutorialSidebar"},{"id":"anatomy-of-a-liveview/render-details","path":"/docs/anatomy-of-a-liveview/render-details","sidebar":"tutorialSidebar"},{"id":"client-javascript/client-hooks","path":"/docs/client-javascript/client-hooks","sidebar":"tutorialSidebar"},{"id":"client-javascript/example-hook","path":"/docs/client-javascript/example-hook","sidebar":"tutorialSidebar"},{"id":"client-javascript/overview","path":"/docs/client-javascript/overview","sidebar":"tutorialSidebar"},{"id":"file-upload/drag-and-drop","path":"/docs/file-upload/drag-and-drop","sidebar":"tutorialSidebar"},{"id":"file-upload/image-preview","path":"/docs/file-upload/image-preview","sidebar":"tutorialSidebar"},{"id":"file-upload/overview","path":"/docs/file-upload/overview","sidebar":"tutorialSidebar"},{"id":"file-upload/upload-config-options","path":"/docs/file-upload/upload-config-options","sidebar":"tutorialSidebar"},{"id":"forms-and-changesets/changesets","path":"/docs/forms-and-changesets/changesets","sidebar":"tutorialSidebar"},{"id":"forms-and-changesets/overview","path":"/docs/forms-and-changesets/overview","sidebar":"tutorialSidebar"},{"id":"forms-and-changesets/use-with-forms","path":"/docs/forms-and-changesets/use-with-forms","sidebar":"tutorialSidebar"},{"id":"js-commands/add-remove-class","path":"/docs/js-commands/add-remove-class","sidebar":"tutorialSidebar"},{"id":"js-commands/dispatch-cmd","path":"/docs/js-commands/dispatch-cmd","sidebar":"tutorialSidebar"},{"id":"js-commands/overview","path":"/docs/js-commands/overview","sidebar":"tutorialSidebar"},{"id":"js-commands/push-cmd","path":"/docs/js-commands/push-cmd","sidebar":"tutorialSidebar"},{"id":"js-commands/set-remove-attr","path":"/docs/js-commands/set-remove-attr","sidebar":"tutorialSidebar"},{"id":"js-commands/show-hide-toggle-el","path":"/docs/js-commands/show-hide-toggle-el","sidebar":"tutorialSidebar"},{"id":"js-commands/transition-cmd","path":"/docs/js-commands/transition-cmd","sidebar":"tutorialSidebar"},{"id":"lifecycle-of-a-liveview/intro","path":"/docs/lifecycle-of-a-liveview/intro","sidebar":"tutorialSidebar"},{"id":"liveview-socket/liveviewsocket-api","path":"/docs/liveview-socket/liveviewsocket-api","sidebar":"tutorialSidebar"},{"id":"liveview-socket/liveviewsocket-api-context","path":"/docs/liveview-socket/liveviewsocket-api-context","sidebar":"tutorialSidebar"},{"id":"liveview-socket/liveviewsocket-api-infos","path":"/docs/liveview-socket/liveviewsocket-api-infos","sidebar":"tutorialSidebar"},{"id":"liveview-socket/liveviewsocket-api-misc","path":"/docs/liveview-socket/liveviewsocket-api-misc","sidebar":"tutorialSidebar"},{"id":"liveview-socket/liveviewsocket-api-push","path":"/docs/liveview-socket/liveviewsocket-api-push","sidebar":"tutorialSidebar"},{"id":"liveview-socket/liveviewsocket-api-uploads","path":"/docs/liveview-socket/liveviewsocket-api-uploads","sidebar":"tutorialSidebar"},{"id":"liveview-socket/raw-liveviewsocket-api","path":"/docs/liveview-socket/raw-liveviewsocket-api","sidebar":"tutorialSidebar"},{"id":"misc/debugging-wire","path":"/docs/misc/debugging-wire","sidebar":"tutorialSidebar"},{"id":"misc/flash","path":"/docs/misc/flash","sidebar":"tutorialSidebar"},{"id":"misc/livecomponents","path":"/docs/misc/livecomponents","sidebar":"tutorialSidebar"},{"id":"misc/root-and-page-renderers","path":"/docs/misc/root-and-page-renderers","sidebar":"tutorialSidebar"},{"id":"misc/security-topics","path":"/docs/misc/security-topics","sidebar":"tutorialSidebar"},{"id":"misc/statics-and-dynamics","path":"/docs/misc/statics-and-dynamics","sidebar":"tutorialSidebar"},{"id":"overview/feedback","path":"/docs/overview/feedback","sidebar":"tutorialSidebar"},{"id":"overview/gratitude","path":"/docs/overview/gratitude","sidebar":"tutorialSidebar"},{"id":"overview/introduction","path":"/docs/overview/introduction","sidebar":"tutorialSidebar"},{"id":"overview/paradigm","path":"/docs/overview/paradigm","sidebar":"tutorialSidebar"},{"id":"overview/runtimes","path":"/docs/overview/runtimes","sidebar":"tutorialSidebar"},{"id":"quick-starts/deno-build-first-liveview","path":"/docs/quick-starts/deno-build-first-liveview","sidebar":"tutorialSidebar"},{"id":"quick-starts/deno-run-examples","path":"/docs/quick-starts/deno-run-examples","sidebar":"tutorialSidebar"},{"id":"quick-starts/get-liveviewjs-repo","path":"/docs/quick-starts/get-liveviewjs-repo","sidebar":"tutorialSidebar"},{"id":"quick-starts/nodejs-build-first-liveview","path":"/docs/quick-starts/nodejs-build-first-liveview","sidebar":"tutorialSidebar"},{"id":"quick-starts/nodejs-run-examples","path":"/docs/quick-starts/nodejs-run-examples","sidebar":"tutorialSidebar"},{"id":"real-time-multi-player-pub-sub/example-pub-sub","path":"/docs/real-time-multi-player-pub-sub/example-pub-sub","sidebar":"tutorialSidebar"},{"id":"real-time-multi-player-pub-sub/overview","path":"/docs/real-time-multi-player-pub-sub/overview","sidebar":"tutorialSidebar"},{"id":"user-events-slash-bindings/additional-bindings","path":"/docs/user-events-slash-bindings/additional-bindings","sidebar":"tutorialSidebar"},{"id":"user-events-slash-bindings/bindings-table","path":"/docs/user-events-slash-bindings/bindings-table","sidebar":"tutorialSidebar"},{"id":"user-events-slash-bindings/overview","path":"/docs/user-events-slash-bindings/overview","sidebar":"tutorialSidebar"},{"id":"user-events-slash-bindings/rate-limiting-bindings","path":"/docs/user-events-slash-bindings/rate-limiting-bindings","sidebar":"tutorialSidebar"},{"id":"webserver-integration/liveview-server-adaptor","path":"/docs/webserver-integration/liveview-server-adaptor","sidebar":"tutorialSidebar"},{"id":"webserver-integration/overview","path":"/docs/webserver-integration/overview","sidebar":"tutorialSidebar"},{"id":"webserver-integration/support-webserver-x","path":"/docs/webserver-integration/support-webserver-x","sidebar":"tutorialSidebar"},{"id":"/category/overview","path":"/docs/category/overview","sidebar":"tutorialSidebar"},{"id":"/category/quick-starts","path":"/docs/category/quick-starts","sidebar":"tutorialSidebar"},{"id":"/category/anatomy-of-a-liveview","path":"/docs/category/anatomy-of-a-liveview","sidebar":"tutorialSidebar"},{"id":"/category/liveviewsocket","path":"/docs/category/liveviewsocket","sidebar":"tutorialSidebar"},{"id":"/category/lifecycle-of-a-liveview","path":"/docs/category/lifecycle-of-a-liveview","sidebar":"tutorialSidebar"},{"id":"/category/user-events","path":"/docs/category/user-events","sidebar":"tutorialSidebar"},{"id":"/category/forms--changesets","path":"/docs/category/forms--changesets","sidebar":"tutorialSidebar"},{"id":"/category/uploading-files","path":"/docs/category/uploading-files","sidebar":"tutorialSidebar"},{"id":"/category/real-time--multi-player","path":"/docs/category/real-time--multi-player","sidebar":"tutorialSidebar"},{"id":"/category/client-side-javascript","path":"/docs/category/client-side-javascript","sidebar":"tutorialSidebar"},{"id":"/category/js-commands","path":"/docs/category/js-commands","sidebar":"tutorialSidebar"},{"id":"/category/webserver-integrations","path":"/docs/category/webserver-integrations","sidebar":"tutorialSidebar"},{"id":"/category/miscellaneous","path":"/docs/category/miscellaneous","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/docs/category/overview","label":"Overview"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var l=n(7529);const s=JSON.parse('{"docusaurusVersion":"2.0.1","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.0.1"},"docusaurus-plugin-content-blog":{"type":"package","name":"@docusaurus/plugin-content-blog","version":"2.0.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.0.1"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"2.0.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.0.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.0.1"},"docusaurus-tailwindcss":{"type":"local"}}}'),c={siteConfig:a.default,siteMetadata:s,globalData:o,i18n:i,codeTranslations:l},u=r.createContext(c);function d(e){let{children:t}=e;return r.createElement(u.Provider,{value:c},t)}},3951:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(2784),a=n(1263),o=n(9854),i=n(9991);function l(e){let{error:t,tryAgain:n}=e;return r.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",height:"50vh",width:"100%",fontSize:"20px"}},r.createElement("h1",null,"This page crashed."),r.createElement("p",null,t.message),r.createElement("button",{type:"button",onClick:n},"Try again"))}function s(e){let{error:t,tryAgain:n}=e;return r.createElement(u,{fallback:()=>r.createElement(l,{error:t,tryAgain:n})},r.createElement(o.Z,null,r.createElement("title",null,"Page Error")),r.createElement(i.Z,null,r.createElement(l,{error:t,tryAgain:n})))}const c=e=>r.createElement(s,e);class u extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){var n;const e={error:t,tryAgain:()=>this.setState({error:null})};return(null!=(n=this.props.fallback)?n:c)(e)}return null!=e?e:null}}},1263:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},9854:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(2784),a=n(2175);function o(e){return r.createElement(a.ql,e)}},9817:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7896),a=n(2784),o=n(7933),i=n(958),l=n(7614),s=n(1344),c=n(1263);const u=a.createContext({collectLink:()=>{}});var d=n(77);function f(e,t){var n,f;let{isNavLink:p,to:m,href:h,activeClassName:g,isActive:v,"data-noBrokenLinkCheck":b,autoAddBaseUrl:y=!0,...w}=e;const{siteConfig:{trailingSlash:k,baseUrl:E}}=(0,l.Z)(),{withBaseUrl:S}=(0,d.C)(),x=(0,a.useContext)(u),_=(0,a.useRef)(null);(0,a.useImperativeHandle)(t,(()=>_.current));const C=m||h;const T=(0,s.Z)(C),L=null==C?void 0:C.replace("pathname://","");let A=void 0!==L?(P=L,y&&(e=>e.startsWith("/"))(P)?S(P):P):void 0;var P;A&&T&&(A=(0,i.applyTrailingSlash)(A,{trailingSlash:k,baseUrl:E}));const R=(0,a.useRef)(!1),N=p?o.OL:o.rU,O=c.Z.canUseIntersectionObserver,D=(0,a.useRef)();(0,a.useEffect)((()=>(!O&&T&&null!=A&&window.docusaurus.prefetch(A),()=>{O&&D.current&&D.current.disconnect()})),[D,A,O,T]);const I=null!=(n=null==(f=A)?void 0:f.startsWith("#"))&&n,M=!A||!T||I;return M||b||x.collectLink(A),M?a.createElement("a",(0,r.Z)({ref:_,href:A},C&&!T&&{target:"_blank",rel:"noopener noreferrer"},w)):a.createElement(N,(0,r.Z)({},w,{onMouseEnter:()=>{R.current||null==A||(window.docusaurus.preload(A),R.current=!0)},innerRef:e=>{_.current=e,O&&e&&T&&(D.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(D.current.unobserve(e),D.current.disconnect(),null!=A&&window.docusaurus.prefetch(A))}))})),D.current.observe(e))},to:A},p&&{isActive:v,activeClassName:g}))}const p=a.forwardRef(f)},1077:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s,I:()=>l});var r=n(2784);function a(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=null==t?void 0:t[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(7529);function i(e){var t,n;let{id:r,message:a}=e;if(void 0===r&&void 0===a)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return null!=(t=null!=(n=o[null!=r?r:a])?n:a)?t:r}function l(e,t){let{message:n,id:r}=e;return a(i({message:n,id:r}),t)}function s(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal <Translate> children",t),new Error("The Docusaurus <Translate> component only accept simple string values");const l=i({message:t,id:n});return r.createElement(r.Fragment,null,a(l,o))}},8183:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});const r="default"},1344:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},77:(e,t,n)=>{"use strict";n.d(t,{C:()=>o,Z:()=>i});var r=n(7614),a=n(1344);function o(){const{siteConfig:{baseUrl:e,url:t}}=(0,r.Z)();return{withBaseUrl:(n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:o=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,a.b)(n))return n;if(o)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const l=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+l:l}(t,e,n,r)}}function i(e,t){void 0===t&&(t={});const{withBaseUrl:n}=o();return n(e,t)}},7614:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(2784),a=n(8874);function o(){return(0,r.useContext)(a._)}},9741:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(2784),a=n(108);function o(){return(0,r.useContext)(a._)}},805:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});function r(e){const t={};return function e(n,r){Object.entries(n).forEach((n=>{let[a,o]=n;const i=r?r+"."+a:a;var l;"object"==typeof(l=o)&&l&&Object.keys(l).length>0?e(o,i):t[i]=o}))}(e),t}},1313:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(2784);const a=r.createContext(null);function o(e){let{children:t,value:n}=e;const o=r.useContext(a),i=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...null==n?void 0:n.data};return{plugin:t.plugin,data:r}}({parent:o,value:n})),[o,n]);return r.createElement(a.Provider,{value:i},t)}},1215:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>h,gA:()=>f,_r:()=>u,Jo:()=>g,zh:()=>d,yW:()=>m,gB:()=>p});var r=n(3181),a=n(7614),o=n(8183);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.Z)();return e}()[e];if(!n&&t.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin.');return n}const l=e=>e.versions.find((e=>e.isLast));function s(e,t){const n=function(e,t){const n=l(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=null==n?void 0:n.docs.find((e=>!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const c={},u=()=>{var e;return null!=(e=i("docusaurus-plugin-content-docs"))?e:c},d=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const r=i(e),a=null==r?void 0:r[t];if(!a&&n.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin with id "'+t+'".');return a}("docusaurus-plugin-content-docs",e,{failfast:!0});function f(e){void 0===e&&(e={});const t=u(),{pathname:n}=(0,r.TH)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error("Can't find active docs plugin for \""+t+'" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: '+Object.values(e).map((e=>e.path)).join(", "));return o}(t,n,e)}function p(e){return d(e).versions}function m(e){const t=d(e);return l(t)}function h(e){const t=d(e),{pathname:n}=(0,r.TH)();return s(t,n)}function g(e){const t=d(e),{pathname:n}=(0,r.TH)();return function(e,t){const n=l(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},879:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={onRouteDidUpdate(e){let{location:t,previousLocation:n}=e;!n||t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash||setTimeout((()=>{window.gtag("event","page_view",{page_title:document.title,page_location:window.location.href,page_path:t.pathname+t.search+t.hash})}))}}},401:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(2521),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},6120:(e,t,n)=>{"use strict";n.r(t);var r=n(7175),a=n(6809);!function(e){const{themeConfig:{prism:t}}=a.default,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{n(4596)("./prism-"+e)})),delete globalThis.Prism}(r.Z)},4442:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(2784);const a="iconExternalLink_awgD";function o(e){let{width:t=13.5,height:n=13.5}=e;return r.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:a},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},9991:(e,t,n)=>{"use strict";n.d(t,{Z:()=>rt});var r=n(2784),a=n(6277),o=n(3951),i=n(328),l=n(211),s=n(9499),c=n(1077),u=n(3181),d=n(2105);function f(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}const p="skipToContent_G6ar";function m(){const{containerRef:e,handleSkip:t}=function(){const e=(0,r.useRef)(null),{action:t}=(0,u.k6)(),n=(0,r.useCallback)((e=>{var t;e.preventDefault();const n=null!=(t=document.querySelector("main:first-of-type"))?t:document.querySelector("."+l.k.wrapper.main);n&&f(n)}),[]);return(0,d.S)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&f(e.current)})),{containerRef:e,handleSkip:n}}();return r.createElement("div",{ref:e,role:"region"},r.createElement("a",{href:"#",className:p,onClick:t},r.createElement(c.Z,{id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"},"Skip to main content")))}var h=n(7683),g=n(3717),v=n(7896);function b(e){let{width:t=21,height:n=21,color:a="currentColor",strokeWidth:o=1.2,className:i,...l}=e;return r.createElement("svg",(0,v.Z)({viewBox:"0 0 15 15",width:t,height:n},l),r.createElement("g",{stroke:a,strokeWidth:o},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const y="announcementBar_ncOr",w="announcementBarPlaceholder_ajMw",k="announcementBarClose_c9u4",E="announcementBarContent__57G";function S(){const{isActive:e,close:t}=(0,g.nT)(),{announcementBar:n}=(0,h.L)();if(!e)return null;const{content:o,backgroundColor:i,textColor:l,isCloseable:s}=n;return r.createElement("div",{className:y,style:{backgroundColor:i,color:l},role:"banner"},s&&r.createElement("div",{className:w}),r.createElement("div",{className:E,dangerouslySetInnerHTML:{__html:o}}),s?r.createElement("button",{type:"button",className:(0,a.Z)("clean-btn close",k),onClick:t,"aria-label":(0,c.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},r.createElement(b,{width:14,height:14,strokeWidth:3.1})):null)}var x=n(7136),_=n(4126);var C=n(6335),T=n(7548);const L=r.createContext(null);function A(e){let{children:t}=e;const n=function(){const e=(0,x.e)(),t=(0,T.HY)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,C.D9)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return r.createElement(L.Provider,{value:n},t)}function P(e){if(e.component){const t=e.component;return r.createElement(t,e.props)}}function R(){const e=(0,r.useContext)(L);if(!e)throw new C.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,T.HY)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:P(o)})),[a,o,t])}function N(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=R();return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var O=n(361),D=n(9741);function I(e){return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function M(e){return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const j={toggle:"toggle_OLSw",toggleButton:"toggleButton_wYmb",darkToggleIcon:"darkToggleIcon_Yem1",lightToggleIcon:"lightToggleIcon_Sxwe",toggleButtonDisabled:"toggleButtonDisabled_vaDU"};function F(e){let{className:t,value:n,onChange:o}=e;const i=(0,D.Z)(),l=(0,c.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===n?(0,c.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,c.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(j.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",j.toggleButton,!i&&j.toggleButtonDisabled),type:"button",onClick:()=>o("dark"===n?"light":"dark"),disabled:!i,title:l,"aria-label":l},r.createElement(I,{className:(0,a.Z)(j.toggleIcon,j.lightToggleIcon)}),r.createElement(M,{className:(0,a.Z)(j.toggleIcon,j.darkToggleIcon)})))}const B=r.memo(F);function z(e){let{className:t}=e;const n=(0,h.L)().colorMode.disableSwitch,{colorMode:a,setColorMode:o}=(0,O.I)();return n?null:r.createElement(B,{className:t,value:a,onChange:o})}var U=n(1881);function q(){return r.createElement(U.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function G(){const e=(0,x.e)();return r.createElement("button",{type:"button",className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},r.createElement(b,{color:"var(--ifm-color-emphasis-600)"}))}function $(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement(q,null),r.createElement(z,{className:"margin-right--md"}),r.createElement(G,null))}var H=n(9817),Z=n(77),V=n(1344);function W(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var Y=n(4442);function K(e){let{activeBasePath:t,activeBaseRegex:n,to:a,href:o,label:i,html:l,isDropdownLink:s,prependBaseUrlToHref:c,...u}=e;const d=(0,Z.Z)(a),f=(0,Z.Z)(t),p=(0,Z.Z)(o,{forcePrependBaseUrl:!0}),m=i&&o&&!(0,V.Z)(o),h=l?{dangerouslySetInnerHTML:{__html:l}}:{children:r.createElement(r.Fragment,null,i,m&&r.createElement(Y.Z,s&&{width:12,height:12}))};return o?r.createElement(H.Z,(0,v.Z)({href:c?p:o},u,h)):r.createElement(H.Z,(0,v.Z)({to:d,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?W(n,t.pathname):t.pathname.startsWith(f)},u,h))}function Q(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=r.createElement(K,(0,v.Z)({className:(0,a.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?r.createElement("li",null,i):i}function X(e){let{className:t,isDropdownItem:n,...o}=e;return r.createElement("li",{className:"menu__list-item"},r.createElement(K,(0,v.Z)({className:(0,a.Z)("menu__link",t)},o)))}function J(e){var t;let{mobile:n=!1,position:a,...o}=e;const i=n?X:Q;return r.createElement(i,(0,v.Z)({},o,{activeClassName:null!=(t=o.activeClassName)?t:n?"menu__link--active":"navbar__link--active"}))}var ee=n(8698),te=n(7661),ne=n(7614);function re(e,t){return e.some((e=>function(e,t){return!!(0,te.Mg)(e.to,t)||!!W(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function ae(e){var t;let{items:n,position:o,className:i,onClick:l,...s}=e;const c=(0,r.useRef)(null),[u,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{c.current&&!c.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)}}),[c]),r.createElement("div",{ref:c,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===o,"dropdown--show":u})},r.createElement(K,(0,v.Z)({"aria-haspopup":"true","aria-expanded":u,role:"button",href:s.to?void 0:"#",className:(0,a.Z)("navbar__link",i)},s,{onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!u))}}),null!=(t=s.children)?t:s.label),r.createElement("ul",{className:"dropdown__menu"},n.map(((e,t)=>r.createElement(be,(0,v.Z)({isDropdownItem:!0,onKeyDown:e=>{if(t===n.length-1&&"Tab"===e.key){e.preventDefault(),d(!1);const t=c.current.nextElementSibling;if(t){(t instanceof HTMLAnchorElement?t:t.querySelector("a")).focus()}}},activeClassName:"dropdown__link--active"},e,{key:t}))))))}function oe(e){var t;let{items:n,className:o,position:i,onClick:l,...s}=e;const c=function(){const{siteConfig:{baseUrl:e}}=(0,ne.Z)(),{pathname:t}=(0,u.TH)();return t.replace(e,"/")}(),d=re(n,c),{collapsed:f,toggleCollapsed:p,setCollapsed:m}=(0,ee.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[c,d,m]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":f})},r.createElement(K,(0,v.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",o)},s,{onClick:e=>{e.preventDefault(),p()}}),null!=(t=s.children)?t:s.label),r.createElement(ee.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:f},n.map(((e,t)=>r.createElement(be,(0,v.Z)({mobile:!0,isDropdownItem:!0,onClick:l,activeClassName:"menu__link--active"},e,{key:t}))))))}function ie(e){let{mobile:t=!1,...n}=e;const a=t?oe:ae;return r.createElement(a,n)}var le=n(5552);function se(e){let{width:t=20,height:n=20,...a}=e;return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},a),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const ce="iconLanguage_xrmG";const ue=()=>null,de="searchBox_xrOJ";function fe(e){let{children:t,className:n}=e;return r.createElement("div",{className:(0,a.Z)(n,de)},t)}var pe=n(1215),me=n(4855);var he=n(7949);const ge=e=>e.docs.find((t=>t.id===e.mainDocId));const ve={default:J,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:a,...o}=e;const{i18n:{currentLocale:i,locales:l,localeConfigs:s}}=(0,ne.Z)(),u=(0,le.l)(),d=[...n,...l.map((e=>{const n="pathname://"+u.createUrl({locale:e,fullyQualified:!1});return{label:s[e].label,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...a],f=t?(0,c.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):s[i].label;return r.createElement(ie,(0,v.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(se,{className:ce}),f),items:d}))},search:function(e){let{mobile:t,className:n}=e;return t?null:r.createElement(fe,{className:n},r.createElement(ue,null))},dropdown:ie,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const l=i?"li":"div";return r.createElement(l,{className:(0,a.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,pe.Iw)(a),l=(0,me.vY)(t,a);return null===l?null:r.createElement(J,(0,v.Z)({exact:!0},o,{isActive:()=>(null==i?void 0:i.path)===l.path||!(null==i||!i.sidebar)&&i.sidebar===l.sidebar,label:null!=n?n:l.id,to:l.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,pe.Iw)(a),l=(0,me.oz)(t,a).link;if(!l)throw new Error('DocSidebarNavbarItem: Sidebar with ID "'+t+"\" doesn't have anything to be linked to.");return r.createElement(J,(0,v.Z)({exact:!0},o,{isActive:()=>(null==i?void 0:i.sidebar)===t,label:null!=n?n:l.label,to:l.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:a,...o}=e;const i=(0,me.lO)(a)[0],l=null!=t?t:i.label,s=null!=n?n:(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return r.createElement(J,(0,v.Z)({},o,{label:l,to:s}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:a,dropdownItemsBefore:o,dropdownItemsAfter:i,...l}=e;const s=(0,pe.Iw)(n),u=(0,pe.gB)(n),{savePreferredVersionName:d}=(0,he.J)(n),f=[...o,...u.map((e=>{var t;const n=null!=(t=s.alternateDocVersions[e.name])?t:ge(e);return{label:e.label,to:n.path,isActive:()=>e===s.activeVersion,onClick:()=>d(e.name)}})),...i],p=(0,me.lO)(n)[0],m=t&&f.length>1?(0,c.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):p.label,h=t&&f.length>1?void 0:ge(p).path;return f.length<=1?r.createElement(J,(0,v.Z)({},l,{mobile:t,label:m,to:h,isActive:a?()=>!1:void 0})):r.createElement(ie,(0,v.Z)({},l,{mobile:t,label:m,to:h,items:f,isActive:a?()=>!1:void 0}))}};function be(e){let{type:t,...n}=e;const a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=ve[a];if(!o)throw new Error('No NavbarItem component found for type "'+t+'".');return r.createElement(o,n)}function ye(){const e=(0,x.e)(),t=(0,h.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map(((t,n)=>r.createElement(be,(0,v.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function we(e){return r.createElement("button",(0,v.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(c.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function ke(){const e=0===(0,h.L)().navbar.items.length,t=R();return r.createElement(r.Fragment,null,!e&&r.createElement(we,{onClick:()=>t.hide()}),t.content)}function Ee(){const e=(0,x.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?r.createElement(N,{header:r.createElement($,null),primaryMenu:r.createElement(ye,null),secondaryMenu:r.createElement(ke,null)}):null}const Se="navbarHideable_bChn",xe="navbarHidden_zsXl";function _e(e){return r.createElement("div",(0,v.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function Ce(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,h.L)(),i=(0,x.e)(),{navbarRef:l,isNavbarVisible:s}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,_.RF)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i<o.current)return void n(!0);if(a.current)return void(a.current=!1);const l=null==r?void 0:r.scrollY,s=document.documentElement.scrollHeight-o.current,c=window.innerHeight;l&&i>=l?n(!1):i+c<s&&n(!0)})),(0,d.S)((t=>{if(e)return t.location.hash?(a.current=!0,void n(!1)):void n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return r.createElement("nav",{ref:l,className:(0,a.Z)("navbar","navbar--fixed-top",n&&[Se,!s&&xe],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,r.createElement(_e,{onClick:i.toggle}),r.createElement(Ee,null))}function Te(e){let{width:t=30,height:n=30,className:a,...o}=e;return r.createElement("svg",(0,v.Z)({className:a,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),r.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function Le(){const e=(0,x.e)();return r.createElement("button",{onClick:e.toggle,onKeyDown:e.toggle,"aria-label":"Navigation bar toggle",className:"navbar__toggle clean-btn",type:"button",tabIndex:0},r.createElement(Te,null))}const Ae="colorModeToggle_Hg9V";function Pe(e){let{items:t}=e;return r.createElement(r.Fragment,null,t.map(((e,t)=>r.createElement(be,(0,v.Z)({},e,{key:t})))))}function Re(e){let{left:t,right:n}=e;return r.createElement("div",{className:"navbar__inner"},r.createElement("div",{className:"navbar__items"},t),r.createElement("div",{className:"navbar__items navbar__items--right"},n))}function Ne(){const e=(0,x.e)(),t=(0,h.L)().navbar.items,[n,a]=function(e){function t(e){var t;return"left"===(null!=(t=e.position)?t:"right")}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return r.createElement(Re,{left:r.createElement(r.Fragment,null,!e.disabled&&r.createElement(Le,null),r.createElement(q,null),r.createElement(Pe,{items:n})),right:r.createElement(r.Fragment,null,r.createElement(Pe,{items:a}),r.createElement(z,{className:Ae}),!o&&r.createElement(fe,null,r.createElement(ue,null)))})}function Oe(){return r.createElement(Ce,null,r.createElement(Ne,null))}function De(e){let{item:t}=e;const{to:n,href:a,label:o,prependBaseUrlToHref:i,...l}=t,s=(0,Z.Z)(n),c=(0,Z.Z)(a,{forcePrependBaseUrl:!0});return r.createElement(H.Z,(0,v.Z)({className:"footer__link-item"},a?{href:i?c:a}:{to:s},l),o,a&&!(0,V.Z)(a)&&r.createElement(Y.Z,null))}function Ie(e){var t;let{item:n}=e;return n.html?r.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:n.html}}):r.createElement("li",{key:null!=(t=n.href)?t:n.to,className:"footer__item"},r.createElement(De,{item:n}))}function Me(e){let{column:t}=e;return r.createElement("div",{className:"col footer__col"},r.createElement("div",{className:"footer__title"},t.title),r.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>r.createElement(Ie,{key:t,item:e})))))}function je(e){let{columns:t}=e;return r.createElement("div",{className:"row footer__links"},t.map(((e,t)=>r.createElement(Me,{key:t,column:e}))))}function Fe(){return r.createElement("span",{className:"footer__link-separator"},"\xb7")}function Be(e){let{item:t}=e;return t.html?r.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement(De,{item:t})}function ze(e){let{links:t}=e;return r.createElement("div",{className:"footer__links text--center"},r.createElement("div",{className:"footer__links"},t.map(((e,n)=>r.createElement(r.Fragment,{key:n},r.createElement(Be,{item:e}),t.length!==n+1&&r.createElement(Fe,null))))))}function Ue(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?r.createElement(je,{columns:t}):r.createElement(ze,{links:t})}var qe=n(3969);const Ge="footerLogoLink_zxYv";function $e(e){var t;let{logo:n}=e;const{withBaseUrl:o}=(0,Z.C)(),i={light:o(n.src),dark:o(null!=(t=n.srcDark)?t:n.src)};return r.createElement(qe.Z,{className:(0,a.Z)("footer__logo",n.className),alt:n.alt,sources:i,width:n.width,height:n.height,style:n.style})}function He(e){let{logo:t}=e;return t.href?r.createElement(H.Z,{href:t.href,className:Ge,target:t.target},r.createElement($e,{logo:t})):r.createElement($e,{logo:t})}function Ze(e){let{copyright:t}=e;return r.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function Ve(e){let{style:t,links:n,logo:o,copyright:i}=e;return r.createElement("footer",{className:(0,a.Z)("footer",{"footer--dark":"dark"===t})},r.createElement("div",{className:"container container-fluid"},n,(o||i)&&r.createElement("div",{className:"footer__bottom text--center"},o&&r.createElement("div",{className:"margin-bottom--sm"},o),i)))}function We(){const{footer:e}=(0,h.L)();if(!e)return null;const{copyright:t,links:n,logo:a,style:o}=e;return r.createElement(Ve,{style:o,links:n&&n.length>0&&r.createElement(Ue,{links:n}),logo:a&&r.createElement(He,{logo:a}),copyright:t&&r.createElement(Ze,{copyright:t})})}const Ye=r.memo(We);var Ke=n(4155);const Qe="docusaurus.tab.",Xe=r.createContext(void 0);const Je=(0,C.Qc)([O.S,g.pl,function(e){let{children:t}=e;const n=function(){const[e,t]=(0,r.useState)({}),n=(0,r.useCallback)(((e,t)=>{(0,Ke.W)("docusaurus.tab."+e).set(t)}),[]);(0,r.useEffect)((()=>{try{const e={};(0,Ke._)().forEach((t=>{if(t.startsWith(Qe)){const n=t.substring(Qe.length);e[n]=(0,Ke.W)(t).get()}})),t(e)}catch(e){console.error(e)}}),[]);const a=(0,r.useCallback)(((e,r)=>{t((t=>({...t,[e]:r}))),n(e,r)}),[n]);return(0,r.useMemo)((()=>({tabGroupChoices:e,setTabGroupChoices:a})),[e,a])}();return r.createElement(Xe.Provider,{value:n},t)},_.OC,he.L5,i.VC,function(e){let{children:t}=e;return r.createElement(T.n2,null,r.createElement(x.M,null,r.createElement(A,null,t)))}]);function et(e){let{children:t}=e;return r.createElement(Je,null,t)}function tt(e){let{error:t,tryAgain:n}=e;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(c.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("p",null,t.message),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},r.createElement(c.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again when the page crashed"},"Try again"))))))}const nt="mainWrapper_UyTV";function rt(e){const{children:t,noFooter:n,wrapperClassName:c,title:u,description:d}=e;return(0,s.t)(),r.createElement(et,null,r.createElement(i.d,{title:u,description:d}),r.createElement(m,null),r.createElement(S,null),r.createElement(Oe,null),r.createElement("div",{className:(0,a.Z)(l.k.wrapper.main,nt,c)},r.createElement(o.Z,{fallback:e=>r.createElement(tt,e)},t)),!n&&r.createElement(Ye,null))}},1881:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var r=n(7896),a=n(2784),o=n(9817),i=n(77),l=n(7614),s=n(7683),c=n(3969);function u(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},l=a.createElement(c.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?a.createElement("div",{className:r},l):l}function d(e){var t;const{siteConfig:{title:n}}=(0,l.Z)(),{navbar:{title:c,logo:d}}=(0,s.L)(),{imageClassName:f,titleClassName:p,...m}=e,h=(0,i.Z)((null==d?void 0:d.href)||"/"),g=c?"":n,v=null!=(t=null==d?void 0:d.alt)?t:g;return a.createElement(o.Z,(0,r.Z)({to:h},m,(null==d?void 0:d.target)&&{target:d.target}),d&&a.createElement(u,{logo:d,alt:v,imageClassName:f}),null!=c&&a.createElement("b",{className:p},c))}},4390:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(2784),a=n(9854);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},3969:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var r=n(7896),a=n(2784),o=n(6277),i=n(9741),l=n(361);const s={themedImage:"themedImage_RWGG","themedImage--light":"themedImage--light_riBm","themedImage--dark":"themedImage--dark_Dsi0"};function c(e){const t=(0,i.Z)(),{colorMode:n}=(0,l.I)(),{sources:c,className:u,alt:d,...f}=e,p=t?"dark"===n?["dark"]:["light"]:["light","dark"];return a.createElement(a.Fragment,null,p.map((e=>a.createElement("img",(0,r.Z)({key:e,src:c[e],alt:d,className:(0,o.Z)(s.themedImage,s["themedImage--"+e],u)},f)))))}},8698:(e,t,n)=>{"use strict";n.d(t,{u:()=>i,z:()=>m});var r=n(7896),a=n(2784),o=n(1263);function i(e){let{initialState:t}=e;const[n,r]=(0,a.useState)(null!=t&&t),o=(0,a.useCallback)((()=>{r((e=>!e))}),[]);return{collapsed:n,setCollapsed:r,toggleCollapsed:o}}const l={display:"none",overflow:"hidden",height:"0px"},s={display:"block",overflow:"visible",height:"auto"};function c(e,t){const n=t?l:s;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function u(e){let{collapsibleRef:t,collapsed:n,animation:r}=e;const o=(0,a.useRef)(!1);(0,a.useEffect)((()=>{const e=t.current;function a(){var t,n;const a=e.scrollHeight,o=null!=(t=null==r?void 0:r.duration)?t:function(e){const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(a);return{transition:"height "+o+"ms "+(null!=(n=null==r?void 0:r.easing)?n:"ease-in-out"),height:a+"px"}}function i(){const t=a();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return c(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(i(),requestAnimationFrame((()=>{e.style.height=l.height,e.style.overflow=l.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{i()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,r])}function d(e){if(!o.Z.canUseDOM)return e?l:s}function f(e){let{as:t="div",collapsed:n,children:r,animation:o,onCollapseTransitionEnd:i,className:l,disableSSRStyle:s}=e;const f=(0,a.useRef)(null);return u({collapsibleRef:f,collapsed:n,animation:o}),a.createElement(t,{ref:f,style:s?void 0:d(n),onTransitionEnd:e=>{"height"===e.propertyName&&(c(f.current,n),null==i||i(n))},className:l},r)}function p(e){let{collapsed:t,...n}=e;const[o,i]=(0,a.useState)(!t),[l,s]=(0,a.useState)(t);return(0,a.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,a.useLayoutEffect)((()=>{o&&s(t)}),[o,t]),o?a.createElement(f,(0,r.Z)({},n,{collapsed:l})):null}function m(e){let{lazy:t,...n}=e;const r=t?p:f;return a.createElement(r,n)}},3717:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>p});var r=n(2784),a=n(9741),o=n(4155),i=n(6335),l=n(7683);const s=(0,o.W)("docusaurus.announcement.dismiss"),c=(0,o.W)("docusaurus.announcement.id"),u=()=>"true"===s.get(),d=e=>s.set(String(e)),f=r.createContext(null);function p(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,l.L)(),t=(0,a.Z)(),[n,o]=(0,r.useState)((()=>!!t&&u()));(0,r.useEffect)((()=>{o(u())}),[]);const i=(0,r.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=c.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;c.set(t),r&&d(!1),!r&&u()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return r.createElement(f.Provider,{value:n},t)}function m(){const e=(0,r.useContext)(f);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},361:(e,t,n)=>{"use strict";n.d(t,{I:()=>g,S:()=>h});var r=n(2784),a=n(1263),o=n(6335),i=n(4155),l=n(7683);const s=r.createContext(void 0),c="theme",u=(0,i.W)(c),d="light",f="dark",p=e=>e===f?f:d;function m(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,l.L)(),[o,i]=(0,r.useState)((e=>a.Z.canUseDOM?p(document.documentElement.getAttribute("data-theme")):p(e))(e));(0,r.useEffect)((()=>{t&&u.del()}),[t]);const s=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(i(t),a&&(e=>{u.set(p(e))})(t)):(i(n?window.matchMedia("(prefers-color-scheme: dark)").matches?f:d:e),u.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",p(o))}),[o]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==c)return;const t=u.get();null!==t&&s(p(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,s]);const m=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||m.current?m.current=window.matchMedia("print").matches:s(null)};return e.addListener(r),()=>e.removeListener(r)}),[s,t,n]),(0,r.useMemo)((()=>({colorMode:o,setColorMode:s,get isDarkTheme(){return o===f},setLightTheme(){s(d)},setDarkTheme(){s(f)}})),[o,s])}function h(e){let{children:t}=e;const n=m();return r.createElement(s.Provider,{value:n},t)}function g(){const e=(0,r.useContext)(s);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},7949:(e,t,n)=>{"use strict";n.d(t,{J:()=>y,L5:()=>v});var r=n(2784),a=n(1215),o=n(8183),i=n(7683),l=n(4855),s=n(6335),c=n(4155);const u=e=>"docs-preferred-version-"+e,d=(e,t,n)=>{(0,c.W)(u(e),{persistence:t}).set(n)},f=(e,t)=>(0,c.W)(u(e),{persistence:t}).get(),p=(e,t)=>{(0,c.W)(u(e),{persistence:t}).del()};const m=r.createContext(null);function h(){const e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,l]=(0,r.useState)((()=>(e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}]))))(n)));(0,r.useEffect)((()=>{l(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=f(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){d(e,t,n),l((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function g(e){let{children:t}=e;const n=h();return r.createElement(m.Provider,{value:n},t)}function v(e){let{children:t}=e;return l.cE?r.createElement(g,null,t):r.createElement(r.Fragment,null,t)}function b(){const e=(0,r.useContext)(m);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function y(e){var t;void 0===e&&(e=o.m);const n=(0,a.zh)(e),[i,l]=b(),{preferredVersionName:s}=i[e];return{preferredVersion:null!=(t=n.versions.find((e=>e.name===s)))?t:null,savePreferredVersionName:(0,r.useCallback)((t=>{l.savePreferredVersion(e,t)}),[l,e])}}},4228:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,b:()=>l});var r=n(2784),a=n(6335);const o=Symbol("EmptyContext"),i=r.createContext(o);function l(e){let{children:t,name:n,items:a}=e;const o=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return r.createElement(i.Provider,{value:o},t)}function s(){const e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},5663:(e,t,n)=>{"use strict";n.d(t,{E:()=>l,q:()=>i});var r=n(2784),a=n(6335);const o=r.createContext(null);function i(e){let{children:t,version:n}=e;return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(null===e)throw new a.i6("DocsVersionProvider");return e}},7136:(e,t,n)=>{"use strict";n.d(t,{M:()=>f,e:()=>p});var r=n(2784),a=n(7548),o=n(7963),i=n(3181),l=n(6335);function s(e){!function(e){const t=(0,i.k6)(),n=(0,l.zX)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var c=n(7683);const u=r.createContext(void 0);function d(){const e=function(){const e=(0,a.HY)(),{items:t}=(0,c.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[i,l]=(0,r.useState)(!1);s((()=>{if(i)return l(!1),!1}));const u=(0,r.useCallback)((()=>{l((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&l(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:u,shown:i})),[e,n,u,i])}function f(e){let{children:t}=e;const n=d();return r.createElement(u.Provider,{value:n},t)}function p(){const e=r.useContext(u);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},7548:(e,t,n)=>{"use strict";n.d(t,{HY:()=>l,Zo:()=>s,n2:()=>i});var r=n(2784),a=n(6335);const o=r.createContext(null);function i(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){let{component:t,props:n}=e;const i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");const[,l]=i,s=(0,a.Ql)(n);return(0,r.useEffect)((()=>{l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((()=>()=>l({component:null,props:null})),[l]),null}},9499:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(2784);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},7963:(e,t,n)=>{"use strict";n.d(t,{i:()=>c});var r=n(2784),a=n(1263);const o="desktop",i="mobile",l="ssr";function s(){return a.Z.canUseDOM?window.innerWidth>996?o:i:l}function c(){const[e,t]=(0,r.useState)((()=>s()));return(0,r.useEffect)((()=>{function e(){t(s())}return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),e}},211:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>"theme-admonition-"+e},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>"theme-doc-sidebar-item-category-level-"+e,docSidebarItemLinkLevel:e=>"theme-doc-sidebar-item-link-level-"+e},blog:{}}},4855:(e,t,n)=>{"use strict";n.d(t,{Wl:()=>h,_F:()=>b,cE:()=>f,jA:()=>g,xz:()=>p,hI:()=>S,lO:()=>w,vY:()=>E,oz:()=>k,s1:()=>y});var r=n(2784),a=n(3181),o=n(9702),i=n(1215),l=n(7949),s=n(5663),c=n(4228);function u(e){return Array.from(new Set(e))}var d=n(7661);const f=!!i._r;function p(e){const t=(0,s.E)();if(!e)return;const n=t.docs[e];if(!n)throw new Error("no version doc found by id="+e);return n}function m(e,t){for(const n of e)if("category"===n.type){if(t(n))return n;const e=m(n.items,t);if(e)return e}}function h(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=h(t);if(e)return e}}}function g(){const{pathname:e}=(0,a.TH)(),t=(0,c.V)();if(!t)throw new Error("Unexpected: cant find current sidebar in context");const n=m(t.items,(t=>(0,d.Mg)(t.href,e)));if(!n)throw new Error(e+" is not associated with a category. useCurrentSidebarCategory() should only be used on category index pages.");return n}const v=(e,t)=>void 0!==e&&(0,d.Mg)(e,t);function b(e,t){return"link"===e.type?v(e.href,t):"category"===e.type&&(v(e.href,t)||((e,t)=>e.some((e=>b(e,t))))(e.items,t))}function y(){var e;const t=(0,c.V)(),{pathname:n}=(0,a.TH)();if(!1===(null==(e=(0,i.gA)())?void 0:e.pluginData.breadcrumbs)||!t)return null;const r=[];return function e(t){for(const a of t)if("category"===a.type&&((0,d.Mg)(a.href,n)||e(a.items))||"link"===a.type&&(0,d.Mg)(a.href,n))return r.push(a),!0;return!1}(t.items),r.reverse()}function w(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,l.J)(e),a=(0,i.yW)(e);return(0,r.useMemo)((()=>u([t,n,a].filter(Boolean))),[t,n,a])}function k(e,t){const n=w(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error("Can't find any sidebar with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((e=>e.name)).join(", ")+'".\n Available sidebar ids are:\n - '+Object.keys(t).join("\n- "));return r[1]}),[e,n])}function E(e,t){const n=w(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error("DocNavbarItem: couldn't find any doc with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((e=>e.name)).join(", ")+'".\nAvailable doc ids are:\n- '+u(t.map((e=>e.id))).join("\n- "))}return r}),[e,n])}function S(e){let{route:t,versionMetadata:n}=e;const r=(0,a.TH)(),i=t.routes,l=i.find((e=>(0,a.LX)(r.pathname,e)));if(!l)return null;const s=l.sidebar,c=s?n.docsSidebars[s]:void 0;return{docElement:(0,o.H)(i),sidebarName:s,sidebarItems:c}}},328:(e,t,n)=>{"use strict";n.d(t,{FG:()=>f,d:()=>u,VC:()=>p});var r=n(2784),a=n(6277),o=n(9854),i=n(1313);function l(){const e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=n(77),c=n(7614);function u(e){let{title:t,description:n,keywords:a,image:i,children:l}=e;const u=function(e){const{siteConfig:t}=(0,c.Z)(),{title:n,titleDelimiter:r}=t;return null!=e&&e.trim().length?e.trim()+" "+r+" "+n:n}(t),{withBaseUrl:d}=(0,s.C)(),f=i?d(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,u),t&&r.createElement("meta",{property:"og:title",content:u}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),f&&r.createElement("meta",{property:"og:image",content:f}),f&&r.createElement("meta",{name:"twitter:image",content:f}),l)}const d=r.createContext(void 0);function f(e){let{className:t,children:n}=e;const i=r.useContext(d),l=(0,a.Z)(i,t);return r.createElement(d.Provider,{value:l},r.createElement(o.Z,null,r.createElement("html",{className:l})),n)}function p(e){let{children:t}=e;const n=l(),o="plugin-"+n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"");const i="plugin-id-"+n.plugin.id;return r.createElement(f,{className:(0,a.Z)(o,i)},t)}},6335:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>c,Ql:()=>s,i6:()=>l,zX:()=>o});var r=n(2784);const a=n(1263).Z.canUseDOM?r.useLayoutEffect:r.useEffect;function o(e){const t=(0,r.useRef)(e);return a((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,r.useRef)();return a((()=>{t.current=e})),t.current}class l extends Error{constructor(e,t){var n,r,a,o;super(),this.name="ReactContextError",this.message="Hook "+(null!=(n=null==(r=this.stack)||null==(a=r.split("\n")[1])||null==(o=a.match(/at (?:\w+\.)?(?<name>\w+)/))?void 0:o.groups.name)?n:"")+" is called outside the <"+e+">. "+(null!=t?t:"")}}function s(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function c(e){return t=>{let{children:n}=t;return r.createElement(r.Fragment,null,e.reduceRight(((e,t)=>r.createElement(t,null,e)),n))}}},7661:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>l});var r=n(2784),a=n(9895),o=n(7614);function i(e,t){const n=e=>{var t;return null==(t=!e||e.endsWith("/")?e:e+"/")?void 0:t.toLowerCase()};return n(e)===n(t)}function l(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>{var t;return null!=(t=e.routes)?t:[]})))}(n)}({routes:a.Z,baseUrl:e})),[e])}},4126:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>f,OC:()=>s,RF:()=>d});var r=n(2784),a=n(1263),o=n(9741),i=n(6335);const l=r.createContext(void 0);function s(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return r.createElement(l.Provider,{value:n},t)}function c(){const e=(0,r.useContext)(l);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const u=()=>a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=c(),a=(0,r.useRef)(u()),o=(0,i.zX)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=u();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function f(){const e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&a<e)&&(t=requestAnimationFrame(r),window.scrollTo(0,Math.floor(.85*(a-e))+e))}(),()=>t&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>null==e.current?void 0:e.current()}}},4925:(e,t,n)=>{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(7614);const r="default";function a(e,t){return"docs-"+e+"-"+t}},4155:(e,t,n)=>{"use strict";n.d(t,{W:()=>l,_:()=>s});const r="localStorage";function a(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,o||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),o=!0),null}var t}let o=!1;const i={get:()=>null,set:()=>{},del:()=>{}};function l(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error('Illegal storage API usage for storage key "'+e+'".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.')}return{get:t,set:t,del:t}}(e);const n=a(null==t?void 0:t.persistence);return null===n?i:{get:()=>{try{return n.getItem(e)}catch(t){return console.error("Docusaurus storage error, can't get key="+e,t),null}},set:t=>{try{n.setItem(e,t)}catch(r){console.error("Docusaurus storage error, can't set "+e+"="+t,r)}},del:()=>{try{n.removeItem(e)}catch(t){console.error("Docusaurus storage error, can't delete key="+e,t)}}}}function s(e){void 0===e&&(e=r);const t=a(e);if(!t)return[];const n=[];for(let r=0;r<t.length;r+=1){const e=t.key(r);null!==e&&n.push(e)}return n}},5552:(e,t,n)=>{"use strict";n.d(t,{l:()=>o});var r=n(7614),a=n(3181);function o(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:n,currentLocale:o}}=(0,r.Z)(),{pathname:i}=(0,a.TH)(),l=o===n?e:e.replace("/"+o+"/","/"),s=i.replace(e,"");return{createUrl:function(e){let{locale:r,fullyQualified:a}=e;return""+(a?t:"")+function(e){return e===n?""+l:""+l+e+"/"}(r)+s}}}},2105:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(2784),a=n(3181),o=n(6335);function i(e){const t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},7683:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(7614);function a(){return(0,r.Z)().siteConfig.themeConfig}},1025:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[a]=e.split(/[#?]/),o="/"===a||a===r?a:(i=a,n?function(e){return e.endsWith("/")?e:e+"/"}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(a,o)}},958:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="post-content";var a=n(1025);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}})},6277:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t<e.length;t++)e[t]&&(n=r(e[t]))&&(a&&(a+=" "),a+=n);else for(t in e)e[t]&&(a&&(a+=" "),a+=t);return a}n.d(t,{Z:()=>a});const a=function(){for(var e,t,n=0,a="";n<arguments.length;)(e=arguments[n++])&&(t=r(e))&&(a&&(a+=" "),a+=t);return a}},4410:(e,t,n)=>{"use strict";n.d(t,{lX:()=>w,q_:()=>C,ob:()=>p,PP:()=>L,Ep:()=>f});var r=n(7896);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r<a;n+=1,r+=1)e[n]=e[r];e.pop()}const i=function(e,t){void 0===t&&(t="");var n,r=e&&e.split("/")||[],i=t&&t.split("/")||[],l=e&&a(e),s=t&&a(t),c=l||s;if(e&&a(e)?i=r:r.length&&(i.pop(),i=i.concat(r)),!i.length)return"/";if(i.length){var u=i[i.length-1];n="."===u||".."===u||""===u}else n=!1;for(var d=0,f=i.length;f>=0;f--){var p=i[f];"."===p?o(i,f):".."===p?(o(i,f),d++):d&&(o(i,f),d--)}if(!c)for(;d--;d)i.unshift("..");!c||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var l=n(1898);function s(e){return"/"===e.charAt(0)?e:"/"+e}function c(e){return"/"===e.charAt(0)?e.substr(1):e}function u(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function f(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function p(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(l){throw l instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):l}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];t.forEach((function(e){return e.apply(void 0,n)}))}}}var h=!("undefined"==typeof window||!window.document||!window.document.createElement);function g(e,t){t(window.confirm(e))}var v="popstate",b="hashchange";function y(){try{return window.history.state||{}}catch(e){return{}}}function w(e){void 0===e&&(e={}),h||(0,l.Z)(!1);var t,n=window.history,a=(-1===(t=window.navigator.userAgent).indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone"))&&window.history&&"pushState"in window.history,o=!(-1===window.navigator.userAgent.indexOf("Trident")),i=e,c=i.forceRefresh,w=void 0!==c&&c,k=i.getUserConfirmation,E=void 0===k?g:k,S=i.keyLength,x=void 0===S?6:S,_=e.basename?d(s(e.basename)):"";function C(e){var t=e||{},n=t.key,r=t.state,a=window.location,o=a.pathname+a.search+a.hash;return _&&(o=u(o,_)),p(o,r,n)}function T(){return Math.random().toString(36).substr(2,x)}var L=m();function A(e){(0,r.Z)(U,e),U.length=n.length,L.notifyListeners(U.location,U.action)}function P(e){(function(e){return void 0===e.state&&-1===navigator.userAgent.indexOf("CriOS")})(e)||O(C(e.state))}function R(){O(C(y()))}var N=!1;function O(e){if(N)N=!1,A();else{L.confirmTransitionTo(e,"POP",E,(function(t){t?A({action:"POP",location:e}):function(e){var t=U.location,n=I.indexOf(t.key);-1===n&&(n=0);var r=I.indexOf(e.key);-1===r&&(r=0);var a=n-r;a&&(N=!0,j(a))}(e)}))}}var D=C(y()),I=[D.key];function M(e){return _+f(e)}function j(e){n.go(e)}var F=0;function B(e){1===(F+=e)&&1===e?(window.addEventListener(v,P),o&&window.addEventListener(b,R)):0===F&&(window.removeEventListener(v,P),o&&window.removeEventListener(b,R))}var z=!1;var U={length:n.length,action:"POP",location:D,createHref:M,push:function(e,t){var r="PUSH",o=p(e,t,T(),U.location);L.confirmTransitionTo(o,r,E,(function(e){if(e){var t=M(o),i=o.key,l=o.state;if(a)if(n.pushState({key:i,state:l},null,t),w)window.location.href=t;else{var s=I.indexOf(U.location.key),c=I.slice(0,s+1);c.push(o.key),I=c,A({action:r,location:o})}else window.location.href=t}}))},replace:function(e,t){var r="REPLACE",o=p(e,t,T(),U.location);L.confirmTransitionTo(o,r,E,(function(e){if(e){var t=M(o),i=o.key,l=o.state;if(a)if(n.replaceState({key:i,state:l},null,t),w)window.location.replace(t);else{var s=I.indexOf(U.location.key);-1!==s&&(I[s]=o.key),A({action:r,location:o})}else window.location.replace(t)}}))},go:j,goBack:function(){j(-1)},goForward:function(){j(1)},block:function(e){void 0===e&&(e=!1);var t=L.setPrompt(e);return z||(B(1),z=!0),function(){return z&&(z=!1,B(-1)),t()}},listen:function(e){var t=L.appendListener(e);return B(1),function(){B(-1),t()}}};return U}var k="hashchange",E={hashbang:{encodePath:function(e){return"!"===e.charAt(0)?e:"!/"+c(e)},decodePath:function(e){return"!"===e.charAt(0)?e.substr(1):e}},noslash:{encodePath:c,decodePath:s},slash:{encodePath:s,decodePath:s}};function S(e){var t=e.indexOf("#");return-1===t?e:e.slice(0,t)}function x(){var e=window.location.href,t=e.indexOf("#");return-1===t?"":e.substring(t+1)}function _(e){window.location.replace(S(window.location.href)+"#"+e)}function C(e){void 0===e&&(e={}),h||(0,l.Z)(!1);var t=window.history,n=(window.navigator.userAgent.indexOf("Firefox"),e),a=n.getUserConfirmation,o=void 0===a?g:a,i=n.hashType,c=void 0===i?"slash":i,v=e.basename?d(s(e.basename)):"",b=E[c],y=b.encodePath,w=b.decodePath;function C(){var e=w(x());return v&&(e=u(e,v)),p(e)}var T=m();function L(e){(0,r.Z)(z,e),z.length=t.length,T.notifyListeners(z.location,z.action)}var A=!1,P=null;function R(){var e,t,n=x(),r=y(n);if(n!==r)_(r);else{var a=C(),i=z.location;if(!A&&(t=a,(e=i).pathname===t.pathname&&e.search===t.search&&e.hash===t.hash))return;if(P===f(a))return;P=null,function(e){if(A)A=!1,L();else{var t="POP";T.confirmTransitionTo(e,t,o,(function(n){n?L({action:t,location:e}):function(e){var t=z.location,n=I.lastIndexOf(f(t));-1===n&&(n=0);var r=I.lastIndexOf(f(e));-1===r&&(r=0);var a=n-r;a&&(A=!0,M(a))}(e)}))}}(a)}}var N=x(),O=y(N);N!==O&&_(O);var D=C(),I=[f(D)];function M(e){t.go(e)}var j=0;function F(e){1===(j+=e)&&1===e?window.addEventListener(k,R):0===j&&window.removeEventListener(k,R)}var B=!1;var z={length:t.length,action:"POP",location:D,createHref:function(e){var t=document.querySelector("base"),n="";return t&&t.getAttribute("href")&&(n=S(window.location.href)),n+"#"+y(v+f(e))},push:function(e,t){var n="PUSH",r=p(e,void 0,void 0,z.location);T.confirmTransitionTo(r,n,o,(function(e){if(e){var t=f(r),a=y(v+t);if(x()!==a){P=t,function(e){window.location.hash=e}(a);var o=I.lastIndexOf(f(z.location)),i=I.slice(0,o+1);i.push(t),I=i,L({action:n,location:r})}else L()}}))},replace:function(e,t){var n="REPLACE",r=p(e,void 0,void 0,z.location);T.confirmTransitionTo(r,n,o,(function(e){if(e){var t=f(r),a=y(v+t);x()!==a&&(P=t,_(a));var o=I.indexOf(f(z.location));-1!==o&&(I[o]=t),L({action:n,location:r})}}))},go:M,goBack:function(){M(-1)},goForward:function(){M(1)},block:function(e){void 0===e&&(e=!1);var t=T.setPrompt(e);return B||(F(1),B=!0),function(){return B&&(B=!1,F(-1)),t()}},listen:function(e){var t=T.appendListener(e);return F(1),function(){F(-1),t()}}};return z}function T(e,t,n){return Math.min(Math.max(e,t),n)}function L(e){void 0===e&&(e={});var t=e,n=t.getUserConfirmation,a=t.initialEntries,o=void 0===a?["/"]:a,i=t.initialIndex,l=void 0===i?0:i,s=t.keyLength,c=void 0===s?6:s,u=m();function d(e){(0,r.Z)(w,e),w.length=w.entries.length,u.notifyListeners(w.location,w.action)}function h(){return Math.random().toString(36).substr(2,c)}var g=T(l,0,o.length-1),v=o.map((function(e){return p(e,void 0,"string"==typeof e?h():e.key||h())})),b=f;function y(e){var t=T(w.index+e,0,w.entries.length-1),r=w.entries[t];u.confirmTransitionTo(r,"POP",n,(function(e){e?d({action:"POP",location:r,index:t}):d()}))}var w={length:v.length,action:"POP",location:v[g],index:g,entries:v,createHref:b,push:function(e,t){var r="PUSH",a=p(e,t,h(),w.location);u.confirmTransitionTo(a,r,n,(function(e){if(e){var t=w.index+1,n=w.entries.slice(0);n.length>t?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=p(e,t,h(),w.location);u.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t<w.entries.length},block:function(e){return void 0===e&&(e=!1),u.setPrompt(e)},listen:function(e){return u.appendListener(e)}};return w}},3463:(e,t,n)=>{"use strict";var r=n(3887),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=p(n);a&&a!==m&&e(t,a,r)}var i=u(n);d&&(i=i.concat(d(n)));for(var l=s(t),h=s(n),g=0;g<i.length;++g){var v=i[g];if(!(o[v]||r&&r[v]||h&&h[v]||l&&l[v])){var b=f(n,v);try{c(t,v,b)}catch(y){}}}}return t}},3459:(e,t)=>{"use strict";var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,a=n?Symbol.for("react.portal"):60106,o=n?Symbol.for("react.fragment"):60107,i=n?Symbol.for("react.strict_mode"):60108,l=n?Symbol.for("react.profiler"):60114,s=n?Symbol.for("react.provider"):60109,c=n?Symbol.for("react.context"):60110,u=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,f=n?Symbol.for("react.forward_ref"):60112,p=n?Symbol.for("react.suspense"):60113,m=n?Symbol.for("react.suspense_list"):60120,h=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,v=n?Symbol.for("react.block"):60121,b=n?Symbol.for("react.fundamental"):60117,y=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function k(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case u:case d:case o:case l:case i:case p:return e;default:switch(e=e&&e.$$typeof){case c:case f:case g:case h:case s:return e;default:return t}}case a:return t}}}function E(e){return k(e)===d}t.AsyncMode=u,t.ConcurrentMode=d,t.ContextConsumer=c,t.ContextProvider=s,t.Element=r,t.ForwardRef=f,t.Fragment=o,t.Lazy=g,t.Memo=h,t.Portal=a,t.Profiler=l,t.StrictMode=i,t.Suspense=p,t.isAsyncMode=function(e){return E(e)||k(e)===u},t.isConcurrentMode=E,t.isContextConsumer=function(e){return k(e)===c},t.isContextProvider=function(e){return k(e)===s},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return k(e)===f},t.isFragment=function(e){return k(e)===o},t.isLazy=function(e){return k(e)===g},t.isMemo=function(e){return k(e)===h},t.isPortal=function(e){return k(e)===a},t.isProfiler=function(e){return k(e)===l},t.isStrictMode=function(e){return k(e)===i},t.isSuspense=function(e){return k(e)===p},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===o||e===d||e===l||e===i||e===p||e===m||"object"==typeof e&&null!==e&&(e.$$typeof===g||e.$$typeof===h||e.$$typeof===s||e.$$typeof===c||e.$$typeof===f||e.$$typeof===b||e.$$typeof===y||e.$$typeof===w||e.$$typeof===v)},t.typeOf=k},3887:(e,t,n)=>{"use strict";e.exports=n(3459)},7677:e=>{"use strict";e.exports=function(e,t,n,r,a,o,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,a,o,i,l],u=0;(s=new Error(t.replace(/%s/g,(function(){return c[u++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},7906:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},1964:(e,t,n)=>{"use strict";n.r(t)},5749:(e,t,n)=>{"use strict";n.r(t)},2521:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'};function a(e,t,n){return e<t?t:e>n?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),c=o.querySelector(r.barSelector),u=r.speed,d=r.easing;return o.offsetWidth,l((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(c,i(e,u,d)),1===e?(s(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){s(o,{transition:"all "+u+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),u)}),u)):setTimeout(t,u)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),l=e?"-100":o(n.status||0),c=document.querySelector(r.parent);return s(i,{transition:"all 0 linear",transform:"translate3d("+l+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&p(a),c!=document.body&&u(c,"nprogress-custom-parent"),c.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&p(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var l=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function c(e,t){return("string"==typeof e?e:f(e)).indexOf(" "+t+" ")>=0}function u(e,t){var n=f(e),r=n+t;c(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=f(e);c(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function f(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function p(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},7320:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,o){for(var i,l,s=a(e),c=1;c<arguments.length;c++){for(var u in i=Object(arguments[c]))n.call(i,u)&&(s[u]=i[u]);if(t){l=t(i);for(var d=0;d<l.length;d++)r.call(i,l[d])&&(s[l[d]]=i[l[d]])}}return s}},99:(e,t,n)=>{var r=n(7906);e.exports=p,e.exports.parse=o,e.exports.compile=function(e,t){return l(o(e,t),t)},e.exports.tokensToFunction=l,e.exports.tokensToRegExp=f;var a=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function o(e,t){for(var n,r=[],o=0,i=0,l="",u=t&&t.delimiter||"/";null!=(n=a.exec(e));){var d=n[0],f=n[1],p=n.index;if(l+=e.slice(i,p),i=p+d.length,f)l+=f[1];else{var m=e[i],h=n[2],g=n[3],v=n[4],b=n[5],y=n[6],w=n[7];l&&(r.push(l),l="");var k=null!=h&&null!=m&&m!==h,E="+"===y||"*"===y,S="?"===y||"*"===y,x=n[2]||u,_=v||b;r.push({name:g||o++,prefix:h||"",delimiter:x,optional:S,repeat:E,partial:k,asterisk:!!w,pattern:_?c(_):w?".*":"[^"+s(x)+"]+?"})}}return i<e.length&&(l+=e.substr(i)),l&&r.push(l),r}function i(e){return encodeURI(e).replace(/[\/?#]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function l(e,t){for(var n=new Array(e.length),a=0;a<e.length;a++)"object"==typeof e[a]&&(n[a]=new RegExp("^(?:"+e[a].pattern+")$",d(t)));return function(t,a){for(var o="",l=t||{},s=(a||{}).pretty?i:encodeURIComponent,c=0;c<e.length;c++){var u=e[c];if("string"!=typeof u){var d,f=l[u.name];if(null==f){if(u.optional){u.partial&&(o+=u.prefix);continue}throw new TypeError('Expected "'+u.name+'" to be defined')}if(r(f)){if(!u.repeat)throw new TypeError('Expected "'+u.name+'" to not repeat, but received `'+JSON.stringify(f)+"`");if(0===f.length){if(u.optional)continue;throw new TypeError('Expected "'+u.name+'" to not be empty')}for(var p=0;p<f.length;p++){if(d=s(f[p]),!n[c].test(d))throw new TypeError('Expected all "'+u.name+'" to match "'+u.pattern+'", but received `'+JSON.stringify(d)+"`");o+=(0===p?u.prefix:u.delimiter)+d}}else{if(d=u.asterisk?encodeURI(f).replace(/[?#]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()})):s(f),!n[c].test(d))throw new TypeError('Expected "'+u.name+'" to match "'+u.pattern+'", but received "'+d+'"');o+=u.prefix+d}}else o+=u}return o}}function s(e){return e.replace(/([.+*?=^!:${}()[\]|\/\\])/g,"\\$1")}function c(e){return e.replace(/([=!:$\/()])/g,"\\$1")}function u(e,t){return e.keys=t,e}function d(e){return e&&e.sensitive?"":"i"}function f(e,t,n){r(t)||(n=t||n,t=[]);for(var a=(n=n||{}).strict,o=!1!==n.end,i="",l=0;l<e.length;l++){var c=e[l];if("string"==typeof c)i+=s(c);else{var f=s(c.prefix),p="(?:"+c.pattern+")";t.push(c),c.repeat&&(p+="(?:"+f+p+")*"),i+=p=c.optional?c.partial?f+"("+p+")?":"(?:"+f+"("+p+"))?":f+"("+p+")"}}var m=s(n.delimiter||"/"),h=i.slice(-m.length)===m;return a||(i=(h?i.slice(0,-m.length):i)+"(?:"+m+"(?=$))?"),i+=o?"$":a&&h?"":"(?="+m+"|$)",u(new RegExp("^"+i,d(n)),t)}function p(e,t,n){return r(t)||(n=t||n,t=[]),n=n||{},e instanceof RegExp?function(e,t){var n=e.source.match(/\((?!\?)/g);if(n)for(var r=0;r<n.length;r++)t.push({name:r,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return u(e,t)}(e,t):r(e)?function(e,t,n){for(var r=[],a=0;a<e.length;a++)r.push(p(e[a],t,n).source);return u(new RegExp("(?:"+r.join("|")+")",d(n)),t)}(e,t,n):function(e,t,n){return f(o(e,n),t,n)}(e,t,n)}},7175:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(t,n){var a,o;switch(n=n||{},r.util.type(t)){case"Object":if(o=r.util.objId(t),n[o])return n[o];for(var i in a={},n[o]=a,t)t.hasOwnProperty(i)&&(a[i]=e(t[i],n));return a;case"Array":return o=r.util.objId(t),n[o]?n[o]:(a=[],n[o]=a,t.forEach((function(t,r){a[r]=e(t,n)})),a);default:return t}},getLanguage:function(t){for(;t;){var n=e.exec(t.className);if(n)return n[1].toLowerCase();t=t.parentElement}return"none"},setLanguage:function(t,n){t.className=t.className.replace(RegExp(e,"gi"),""),t.classList.add("language-"+n)},isActive:function(e,t,n){for(var r="no-"+t;e;){var a=e.classList;if(a.contains(t))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!n}},languages:{plain:n,plaintext:n,text:n,txt:n,extend:function(e,t){var n=r.util.clone(r.languages[e]);for(var a in t)n[a]=t[a];return n},insertBefore:function(e,t,n,a){var o=(a=a||r.languages)[e],i={};for(var l in o)if(o.hasOwnProperty(l)){if(l==t)for(var s in n)n.hasOwnProperty(s)&&(i[s]=n[s]);n.hasOwnProperty(l)||(i[l]=o[l])}var c=a[e];return a[e]=i,r.languages.DFS(r.languages,(function(t,n){n===c&&t!=e&&(this[t]=i)})),i},DFS:function e(t,n,a,o){o=o||{};var i=r.util.objId;for(var l in t)if(t.hasOwnProperty(l)){n.call(t,l,t[l],a||l);var s=t[l],c=r.util.type(s);"Object"!==c||o[i(s)]?"Array"!==c||o[i(s)]||(o[i(s)]=!0,e(s,n,l,o)):(o[i(s)]=!0,e(s,n,null,o))}}},plugins:{},highlight:function(e,t,n){var o={code:e,grammar:t,language:n};return r.hooks.run("before-tokenize",o),o.tokens=r.tokenize(o.code,o.grammar),r.hooks.run("after-tokenize",o),a.stringify(r.util.encode(o.tokens),o.language)},tokenize:function(e,t){var n=t.rest;if(n){for(var r in n)t[r]=n[r];delete t.rest}var a=new l;return s(a,a.head,e),i(e,a,t,a.head,0),function(e){var t=[],n=e.head.next;for(;n!==e.tail;)t.push(n.value),n=n.next;return t}(a)},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var a,o=0;a=n[o++];)a(t)}},Token:a};function a(e,t,n,r){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length}function o(e,t,n,r){e.lastIndex=t;var a=e.exec(n);if(a&&r&&a[1]){var o=a[1].length;a.index+=o,a[0]=a[0].slice(o)}return a}function i(e,t,n,l,u,d){for(var f in n)if(n.hasOwnProperty(f)&&n[f]){var p=n[f];p=Array.isArray(p)?p:[p];for(var m=0;m<p.length;++m){if(d&&d.cause==f+","+m)return;var h=p[m],g=h.inside,v=!!h.lookbehind,b=!!h.greedy,y=h.alias;if(b&&!h.pattern.global){var w=h.pattern.toString().match(/[imsuy]*$/)[0];h.pattern=RegExp(h.pattern.source,w+"g")}for(var k=h.pattern||h,E=l.next,S=u;E!==t.tail&&!(d&&S>=d.reach);S+=E.value.length,E=E.next){var x=E.value;if(t.length>e.length)return;if(!(x instanceof a)){var _,C=1;if(b){if(!(_=o(k,S,e,v))||_.index>=e.length)break;var T=_.index,L=_.index+_[0].length,A=S;for(A+=E.value.length;T>=A;)A+=(E=E.next).value.length;if(S=A-=E.value.length,E.value instanceof a)continue;for(var P=E;P!==t.tail&&(A<L||"string"==typeof P.value);P=P.next)C++,A+=P.value.length;C--,x=e.slice(S,A),_.index-=S}else if(!(_=o(k,0,x,v)))continue;T=_.index;var R=_[0],N=x.slice(0,T),O=x.slice(T+R.length),D=S+x.length;d&&D>d.reach&&(d.reach=D);var I=E.prev;if(N&&(I=s(t,I,N),S+=N.length),c(t,I,C),E=s(t,I,new a(f,g?r.tokenize(R,g):R,y,R)),O&&s(t,E,O),C>1){var M={cause:f+","+m,reach:D};i(e,t,n,E.prev,S,M),d&&M.reach>d.reach&&(d.reach=M.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function c(e,t,n){for(var r=t.next,a=0;a<n&&r!==e.tail;a++)r=r.next;t.next=r,r.prev=t,e.length-=a}return a.stringify=function e(t,n){if("string"==typeof t)return t;if(Array.isArray(t)){var a="";return t.forEach((function(t){a+=e(t,n)})),a}var o={type:t.type,content:e(t.content,n),tag:"span",classes:["token",t.type],attributes:{},language:n},i=t.alias;i&&(Array.isArray(i)?Array.prototype.push.apply(o.classes,i):o.classes.push(i)),r.hooks.run("wrap",o);var l="";for(var s in o.attributes)l+=" "+s+'="'+(o.attributes[s]||"").replace(/"/g,""")+'"';return"<"+o.tag+' class="'+o.classes.join(" ")+'"'+l+">"+o.content+"</"+o.tag+">"},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^<!\[CDATA\[|\]\]>$/i;var r={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i<a.length;i++)o[a[i]]=e.languages.bash[a[i]];e.languages.shell=e.languages.bash}(a),a.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!<keyword>)\w+(?:\s*\.\s*\w+)*\b/.source.replace(/<keyword>/g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!<keyword>)\w+/.source.replace(/<keyword>/g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/<mod-name>(?:\s*:\s*<mod-name>)?|:\s*<mod-name>/.source.replace(/<mod-name>/g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-]<PLAIN>)(?:[ \t]*(?:(?![#:])<PLAIN>|:<PLAIN>))*/.source.replace(/<PLAIN>/g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<<prop>>[ \t]+)?)(?:<<value>>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<<prop>>/g,(function(){return r})).replace(/<<value>>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<<prop>>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<<prop>>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<<prop>>[ \t]+)?)<<key>>(?=\s*:\s)/.source.replace(/<<prop>>/g,(function(){return r})).replace(/<<key>>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(/<inner>/g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)<inner>|_(?:(?!_)<inner>)+_)+__\b|\*\*(?:(?!\*)<inner>|\*(?:(?!\*)<inner>)+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)<inner>|__(?:(?!_)<inner>)+__)+_\b|\*(?:(?!\*)<inner>|\*\*(?:(?!\*)<inner>)+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~)<inner>)+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\])<inner>)+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\])<inner>)+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n<r;n++){var a=t[n];if("code"===a.type){var o=a.content[1],i=a.content[3];if(o&&i&&"code-language"===o.type&&"code-block"===i.type&&"string"==typeof o.content){var l=o.content.replace(/\b#/g,"sharp").replace(/\b\+\+/g,"pp"),s="language-"+(l=(/[a-z][\w-]*/i.exec(l)||[""])[0].toLowerCase());i.alias?"string"==typeof i.alias?i.alias=[i.alias,s]:i.alias.push(s):i.alias=[s]}}else e(a.content)}}(e.tokens)})),e.hooks.add("wrap",(function(t){if("code-block"===t.type){for(var n="",r=0,a=t.classes.length;r<a;r++){var o=t.classes[r],c=/language-(.+)/.exec(o);if(c){n=c[1];break}}var u,d=e.languages[n];if(d)t.content=e.highlight((u=t.content,u.replace(i,"").replace(/&(\w{1,8}|#x?[\da-f]{1,8});/gi,(function(e,t){var n;if("#"===(t=t.toLowerCase())[0])return n="x"===t[1]?parseInt(t.slice(2),16):Number(t.slice(1)),s(n);var r=l[t];return r||e}))),d,n);else if(n&&"none"!==n&&e.plugins.autoloader){var f="md-"+(new Date).valueOf()+"-"+Math.floor(1e16*Math.random());t.attributes.id=f,e.plugins.autoloader.loadLanguages(n,(function(){var t=document.getElementById(f);t&&(t.innerHTML=e.highlight(t.textContent,e.languages[n],n))}))}}}));var i=RegExp(e.languages.markup.tag.pattern.source,"gi"),l={amp:"&",lt:"<",gt:">",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n<t.length;){var r=t[n++];if("keyword"===r.type&&"mutation"===r.content){var a=[];if(d(["definition-mutation","punctuation"])&&"("===u(1).content){n+=2;var o=f(/^\($/,/^\)$/);if(-1===o)continue;for(;n<o;n++){var i=u(0);"variable"===i.type&&(p(i,"variable-input"),a.push(i.content))}n=o+1}if(d(["punctuation","property-query"])&&"{"===u(0).content&&(n++,p(u(0),"property-mutation"),a.length>0)){var l=f(/^\{$/,/^\}$/);if(-1===l)continue;for(var s=n;s<l;s++){var c=t[s];"variable"===c.type&&a.indexOf(c.content)>=0&&p(c,"variable-input")}}}}function u(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n<e.length;n++){var r=u(n+t);if(!r||r.type!==e[n])return!1}return!0}function f(e,r){for(var a=1,o=n;o<t.length;o++){var i=t[o],l=i.content;if("punctuation"===i.type&&"string"==typeof l)if(e.test(l))a++;else if(r.test(l)&&0===--a)return o}return-1}function p(e,t){var n=e.alias;n?Array.isArray(n)||(e.alias=n=[n]):e.alias=n=[],n.push(t)}})),a.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function l(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function c(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,s(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function u(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,u={},d=s(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=l(i++,r)););return u[n]=a,n})).join(""),n,r),f=Object.keys(u);return i=0,function e(t){for(var n=0;n<t.length;n++){if(i>=f.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=f[i],o="string"==typeof r?r:r.content,l=o.indexOf(a);if(-1!==l){++i;var s=o.substring(0,l),d=c(u[a]),p=o.substring(l+a.length),m=[];if(s&&m.push(s),m.push(d),p){var h=[p];e(h),m.push.apply(m,h)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var g=r.content;Array.isArray(g)?e(g):e([g])}}}(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function f(e){return"string"==typeof e?e:Array.isArray(e)?e.map(f).join(""):f(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var r=0,a=n.length;r<a;r++){var o=n[r];if("string"!=typeof o){var i=o.content;if(Array.isArray(i))if("template-string"===o.type){var l=i[1];if(3===i.length&&"string"!=typeof l&&"embedded-code"===l.type){var s=f(l),c=l.alias,d=Array.isArray(c)?c[0]:c,p=e.languages[d];if(!p)continue;i[1]=u(s,p,d)}}else t(i);else"string"!=typeof i&&t([i])}}}(t.tokens)}))}(a),function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(/<ID>/g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:<ID>(?:\s*,\s*(?:\*\s*as\s+<ID>|\{[^{}]*\}))?|\*\s*as\s+<ID>|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+<ID>)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?<ID>/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r<n.length;r++){var a=n[r],o=e.languages.javascript[a];"RegExp"===e.util.type(o)&&(o=e.languages.javascript[a]={pattern:o});var i=o.inside||{};o.inside=i,i["maybe-class-name"]=/^[A-Z][\s\S]*/}}(a),function(e){var t=e.util.clone(e.languages.javascript),n=/(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source,r=/(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source,a=/(?:\{<S>*\.{3}(?:[^{}]|<BRACES>)*\})/.source;function o(e,t){return e=e.replace(/<S>/g,(function(){return n})).replace(/<BRACES>/g,(function(){return r})).replace(/<SPREAD>/g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:<S>+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|<BRACES>))?|<SPREAD>))*<S>*\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(/<SPREAD>/.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=<BRACES>/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},l=function(t){for(var n=[],r=0;r<t.length;r++){var a=t[r],o=!1;if("string"!=typeof a&&("tag"===a.type&&a.content[0]&&"tag"===a.content[0].type?"</"===a.content[0].content[0].content?n.length>0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var s=i(a);r<t.length-1&&("string"==typeof t[r+1]||"plain-text"===t[r+1].type)&&(s+=i(t[r+1]),t.splice(r+1,1)),r>0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(s=i(t[r-1])+s,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",s,null,s)}a.content&&"string"!=typeof a.content&&l(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||l(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,l=i.length;-1!==n.code.indexOf(a=t(r,l));)++l;return i[l]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(l){for(var s=0;s<l.length&&!(a>=o.length);s++){var c=l[s];if("string"==typeof c||c.content&&"string"==typeof c.content){var u=o[a],d=n.tokenStack[u],f="string"==typeof c?c:c.content,p=t(r,u),m=f.indexOf(p);if(m>-1){++a;var h=f.substring(0,m),g=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),v=f.substring(m+p.length),b=[];h&&b.push.apply(b,i([h])),b.push(g),v&&b.push.apply(b,i([v])),"string"==typeof c?l.splice.apply(l,[s,1].concat(b)):c.content=b}}else c.content&&i(c.content)}return l}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},9528:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:["sh","shell"],aliasTitles:{sh:"Shell",shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bbj:{title:"BBj",owner:"hyyan"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},bqn:{title:"BQN",owner:"yewscion"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},cilkc:{title:"Cilk/C",require:"c",alias:"cilk-c",owner:"OpenCilk"},cilkcpp:{title:"Cilk/C++",require:"cpp",alias:["cilk-cpp","cilk"],owner:"OpenCilk"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},gradle:{title:"Gradle",require:"clike",owner:"zeabdelkhalek-badido18"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},metafont:{title:"METAFONT",owner:"LaeriExNihilo"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (SCSS)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wgsl:{title:"WGSL",owner:"Dr4gonthree"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to <a href="https://webplatform.github.io/docs/">WebPlatform.org documentation</a>. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (<code>.comment</code> can become <code>.namespace--comment</code>) or replace them with your defined ones (like <code>.editor__comment</code>). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the <code>highlightAll</code> and <code>highlightAllUnder</code> methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},5525:(e,t,n)=>{const r=n(9528),a=n(4515),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(4596).resolve(t)],delete Prism.languages[e],n(4596)(t),o.add(e)}))}i.silent=!1,e.exports=i},4596:(e,t,n)=>{var r={"./":5525};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=4596},4515:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n<r;n++)t[e[n]]=!0;return t}function r(e){var n={},r=[];function a(r,o){if(!(r in n)){o.push(r);var i=o.indexOf(r);if(i<o.length-1)throw new Error("Circular dependency: "+o.slice(i).join(" -> "));var l={},s=e[r];if(s){function c(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in l))for(var i in a(t,o),l[t]=!0,n[t])l[i]=!0}t(s.require,c),t(s.optional,c),t(s.modify,c)}n[r]=l,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,l){var s=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),c=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(s);i=i.map(c),l=(l||[]).map(c);var u=n(i),d=n(l);i.forEach((function e(n){var r=s[n];t(r&&r.require,(function(t){t in d||(u[t]=!0,e(t))}))}));for(var f,p=r(s),m=u;a(m);){for(var h in f={},m){var g=s[h];t(g&&g.modify,(function(e){e in d&&(f[e]=!0)}))}for(var v in d)if(!(v in u))for(var b in p(v))if(b in u){f[v]=!0;break}for(var y in m=f)u[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,l={},s={};function c(e){if(e in l)return l[e];s[e]=!0;var a,u=[];for(var d in t(e))d in n&&u.push(d);if(0===u.length)a=r(e);else{var f=i(u.map((function(e){var t=c(e);return delete s[e],t})));o?a=o(f,(function(){return r(e)})):r(e)}return l[e]=a}for(var u in n)c(u);var d=[];for(var f in s)d.push(l[f]);return i(d)}(p,u,t,n)}};return w}}();e.exports=t},8262:(e,t,n)=>{"use strict";var r=n(3586);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},3980:(e,t,n)=>{e.exports=n(8262)()},3586:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},2967:(e,t,n)=>{"use strict";var r=n(2784),a=n(7320),o=n(4616);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}if(!r)throw Error(i(227));var l=new Set,s={};function c(e,t){u(e,t),u(e+"Capture",t)}function u(e,t){for(s[e]=t,e=0;e<t.length;e++)l.add(t[e])}var d=!("undefined"==typeof window||void 0===window.document||void 0===window.document.createElement),f=/^[: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]*$/,p=Object.prototype.hasOwnProperty,m={},h={};function g(e,t,n,r,a,o,i){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var v={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(e){v[e]=new g(e,0,!1,e,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(e){var t=e[0];v[t]=new g(t,1,!1,e[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(e){v[e]=new g(e,2,!1,e.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(e){v[e]=new g(e,2,!1,e,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(e){v[e]=new g(e,3,!1,e.toLowerCase(),null,!1,!1)})),["checked","multiple","muted","selected"].forEach((function(e){v[e]=new g(e,3,!0,e,null,!1,!1)})),["capture","download"].forEach((function(e){v[e]=new g(e,4,!1,e,null,!1,!1)})),["cols","rows","size","span"].forEach((function(e){v[e]=new g(e,6,!1,e,null,!1,!1)})),["rowSpan","start"].forEach((function(e){v[e]=new g(e,5,!1,e.toLowerCase(),null,!1,!1)}));var b=/[\-:]([a-z])/g;function y(e){return e[1].toUpperCase()}function w(e,t,n,r){var a=v.hasOwnProperty(t)?v[t]:null;(null!==a?0===a.type:!r&&(2<t.length&&("o"===t[0]||"O"===t[0])&&("n"===t[1]||"N"===t[1])))||(function(e,t,n,r){if(null==t||function(e,t,n,r){if(null!==n&&0===n.type)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return!r&&(null!==n?!n.acceptsBooleans:"data-"!==(e=e.toLowerCase().slice(0,5))&&"aria-"!==e);default:return!1}}(e,t,n,r))return!0;if(r)return!1;if(null!==n)switch(n.type){case 3:return!t;case 4:return!1===t;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}(t,n,a,r)&&(n=null),r||null===a?function(e){return!!p.call(h,e)||!p.call(m,e)&&(f.test(e)?h[e]=!0:(m[e]=!0,!1))}(t)&&(null===n?e.removeAttribute(t):e.setAttribute(t,""+n)):a.mustUseProperty?e[a.propertyName]=null===n?3!==a.type&&"":n:(t=a.attributeName,r=a.attributeNamespace,null===n?e.removeAttribute(t):(n=3===(a=a.type)||4===a&&!0===n?"":""+n,r?e.setAttributeNS(r,t,n):e.setAttribute(t,n))))}"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(e){var t=e.replace(b,y);v[t]=new g(t,1,!1,e,null,!1,!1)})),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach((function(e){var t=e.replace(b,y);v[t]=new g(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)})),["xml:base","xml:lang","xml:space"].forEach((function(e){var t=e.replace(b,y);v[t]=new g(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)})),["tabIndex","crossOrigin"].forEach((function(e){v[e]=new g(e,1,!1,e.toLowerCase(),null,!1,!1)})),v.xlinkHref=new g("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach((function(e){v[e]=new g(e,1,!1,e.toLowerCase(),null,!0,!0)}));var k=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,E=60103,S=60106,x=60107,_=60108,C=60114,T=60109,L=60110,A=60112,P=60113,R=60120,N=60115,O=60116,D=60121,I=60128,M=60129,j=60130,F=60131;if("function"==typeof Symbol&&Symbol.for){var B=Symbol.for;E=B("react.element"),S=B("react.portal"),x=B("react.fragment"),_=B("react.strict_mode"),C=B("react.profiler"),T=B("react.provider"),L=B("react.context"),A=B("react.forward_ref"),P=B("react.suspense"),R=B("react.suspense_list"),N=B("react.memo"),O=B("react.lazy"),D=B("react.block"),B("react.scope"),I=B("react.opaque.id"),M=B("react.debug_trace_mode"),j=B("react.offscreen"),F=B("react.legacy_hidden")}var z,U="function"==typeof Symbol&&Symbol.iterator;function q(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=U&&e[U]||e["@@iterator"])?e:null}function G(e){if(void 0===z)try{throw Error()}catch(n){var t=n.stack.trim().match(/\n( *(at )?)/);z=t&&t[1]||""}return"\n"+z+e}var $=!1;function H(e,t){if(!e||$)return"";$=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),"object"==typeof Reflect&&Reflect.construct){try{Reflect.construct(t,[])}catch(s){var r=s}Reflect.construct(e,[],t)}else{try{t.call()}catch(s){r=s}e.call(t.prototype)}else{try{throw Error()}catch(s){r=s}e()}}catch(s){if(s&&r&&"string"==typeof s.stack){for(var a=s.stack.split("\n"),o=r.stack.split("\n"),i=a.length-1,l=o.length-1;1<=i&&0<=l&&a[i]!==o[l];)l--;for(;1<=i&&0<=l;i--,l--)if(a[i]!==o[l]){if(1!==i||1!==l)do{if(i--,0>--l||a[i]!==o[l])return"\n"+a[i].replace(" at new "," at ")}while(1<=i&&0<=l);break}}}finally{$=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?G(e):""}function Z(e){switch(e.tag){case 5:return G(e.type);case 16:return G("Lazy");case 13:return G("Suspense");case 19:return G("SuspenseList");case 0:case 2:case 15:return e=H(e.type,!1);case 11:return e=H(e.type.render,!1);case 22:return e=H(e.type._render,!1);case 1:return e=H(e.type,!0);default:return""}}function V(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case x:return"Fragment";case S:return"Portal";case C:return"Profiler";case _:return"StrictMode";case P:return"Suspense";case R:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case L:return(e.displayName||"Context")+".Consumer";case T:return(e._context.displayName||"Context")+".Provider";case A:var t=e.render;return t=t.displayName||t.name||"",e.displayName||(""!==t?"ForwardRef("+t+")":"ForwardRef");case N:return V(e.type);case D:return V(e._render);case O:t=e._payload,e=e._init;try{return V(e(t))}catch(n){}}return null}function W(e){switch(typeof e){case"boolean":case"number":case"object":case"string":case"undefined":return e;default:return""}}function Y(e){var t=e.type;return(e=e.nodeName)&&"input"===e.toLowerCase()&&("checkbox"===t||"radio"===t)}function K(e){e._valueTracker||(e._valueTracker=function(e){var t=Y(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&void 0!==n&&"function"==typeof n.get&&"function"==typeof n.set){var a=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(e){r=""+e,o.call(this,e)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(e){r=""+e},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}(e))}function Q(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Y(e)?e.checked?"true":"false":e.value),(e=r)!==n&&(t.setValue(e),!0)}function X(e){if(void 0===(e=e||("undefined"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}function J(e,t){var n=t.checked;return a({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=n?n:e._wrapperState.initialChecked})}function ee(e,t){var n=null==t.defaultValue?"":t.defaultValue,r=null!=t.checked?t.checked:t.defaultChecked;n=W(null!=t.value?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:"checkbox"===t.type||"radio"===t.type?null!=t.checked:null!=t.value}}function te(e,t){null!=(t=t.checked)&&w(e,"checked",t,!1)}function ne(e,t){te(e,t);var n=W(t.value),r=t.type;if(null!=n)"number"===r?(0===n&&""===e.value||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if("submit"===r||"reset"===r)return void e.removeAttribute("value");t.hasOwnProperty("value")?ae(e,t.type,n):t.hasOwnProperty("defaultValue")&&ae(e,t.type,W(t.defaultValue)),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked)}function re(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!("submit"!==r&&"reset"!==r||void 0!==t.value&&null!==t.value))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}""!==(n=e.name)&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,""!==n&&(e.name=n)}function ae(e,t,n){"number"===t&&X(e.ownerDocument)===e||(null==n?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}function oe(e,t){return e=a({children:void 0},t),(t=function(e){var t="";return r.Children.forEach(e,(function(e){null!=e&&(t+=e)})),t}(t.children))&&(e.children=t),e}function ie(e,t,n,r){if(e=e.options,t){t={};for(var a=0;a<n.length;a++)t["$"+n[a]]=!0;for(n=0;n<e.length;n++)a=t.hasOwnProperty("$"+e[n].value),e[n].selected!==a&&(e[n].selected=a),a&&r&&(e[n].defaultSelected=!0)}else{for(n=""+W(n),t=null,a=0;a<e.length;a++){if(e[a].value===n)return e[a].selected=!0,void(r&&(e[a].defaultSelected=!0));null!==t||e[a].disabled||(t=e[a])}null!==t&&(t.selected=!0)}}function le(e,t){if(null!=t.dangerouslySetInnerHTML)throw Error(i(91));return a({},t,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue})}function se(e,t){var n=t.value;if(null==n){if(n=t.children,t=t.defaultValue,null!=n){if(null!=t)throw Error(i(92));if(Array.isArray(n)){if(!(1>=n.length))throw Error(i(93));n=n[0]}t=n}null==t&&(t=""),n=t}e._wrapperState={initialValue:W(n)}}function ce(e,t){var n=W(t.value),r=W(t.defaultValue);null!=n&&((n=""+n)!==e.value&&(e.value=n),null==t.defaultValue&&e.defaultValue!==n&&(e.defaultValue=n)),null!=r&&(e.defaultValue=""+r)}function ue(e){var t=e.textContent;t===e._wrapperState.initialValue&&""!==t&&null!==t&&(e.value=t)}var de="http://www.w3.org/1999/xhtml",fe="http://www.w3.org/2000/svg";function pe(e){switch(e){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 me(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?pe(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}var he,ge,ve=(ge=function(e,t){if(e.namespaceURI!==fe||"innerHTML"in e)e.innerHTML=t;else{for((he=he||document.createElement("div")).innerHTML="<svg>"+t.valueOf().toString()+"</svg>",t=he.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,t,n,r){MSApp.execUnsafeLocalFunction((function(){return ge(e,t)}))}:ge);function be(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}var ye={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},we=["Webkit","ms","Moz","O"];function ke(e,t,n){return null==t||"boolean"==typeof t||""===t?"":n||"number"!=typeof t||0===t||ye.hasOwnProperty(e)&&ye[e]?(""+t).trim():t+"px"}function Ee(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),a=ke(n,t[n],r);"float"===n&&(n="cssFloat"),r?e.setProperty(n,a):e[n]=a}}Object.keys(ye).forEach((function(e){we.forEach((function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),ye[t]=ye[e]}))}));var Se=a({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 xe(e,t){if(t){if(Se[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML))throw Error(i(137,e));if(null!=t.dangerouslySetInnerHTML){if(null!=t.children)throw Error(i(60));if("object"!=typeof t.dangerouslySetInnerHTML||!("__html"in t.dangerouslySetInnerHTML))throw Error(i(61))}if(null!=t.style&&"object"!=typeof t.style)throw Error(i(62))}}function _e(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){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 Ce(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}var Te=null,Le=null,Ae=null;function Pe(e){if(e=ra(e)){if("function"!=typeof Te)throw Error(i(280));var t=e.stateNode;t&&(t=oa(t),Te(e.stateNode,e.type,t))}}function Re(e){Le?Ae?Ae.push(e):Ae=[e]:Le=e}function Ne(){if(Le){var e=Le,t=Ae;if(Ae=Le=null,Pe(e),t)for(e=0;e<t.length;e++)Pe(t[e])}}function Oe(e,t){return e(t)}function De(e,t,n,r,a){return e(t,n,r,a)}function Ie(){}var Me=Oe,je=!1,Fe=!1;function Be(){null===Le&&null===Ae||(Ie(),Ne())}function ze(e,t){var n=e.stateNode;if(null===n)return null;var r=oa(n);if(null===r)return null;n=r[t];e:switch(t){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(r=!r.disabled)||(r=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&"function"!=typeof n)throw Error(i(231,t,typeof n));return n}var Ue=!1;if(d)try{var qe={};Object.defineProperty(qe,"passive",{get:function(){Ue=!0}}),window.addEventListener("test",qe,qe),window.removeEventListener("test",qe,qe)}catch(ge){Ue=!1}function Ge(e,t,n,r,a,o,i,l,s){var c=Array.prototype.slice.call(arguments,3);try{t.apply(n,c)}catch(u){this.onError(u)}}var $e=!1,He=null,Ze=!1,Ve=null,We={onError:function(e){$e=!0,He=e}};function Ye(e,t,n,r,a,o,i,l,s){$e=!1,He=null,Ge.apply(We,arguments)}function Ke(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do{0!=(1026&(t=e).flags)&&(n=t.return),e=t.return}while(e)}return 3===t.tag?n:null}function Qe(e){if(13===e.tag){var t=e.memoizedState;if(null===t&&(null!==(e=e.alternate)&&(t=e.memoizedState)),null!==t)return t.dehydrated}return null}function Xe(e){if(Ke(e)!==e)throw Error(i(188))}function Je(e){if(e=function(e){var t=e.alternate;if(!t){if(null===(t=Ke(e)))throw Error(i(188));return t!==e?null:e}for(var n=e,r=t;;){var a=n.return;if(null===a)break;var o=a.alternate;if(null===o){if(null!==(r=a.return)){n=r;continue}break}if(a.child===o.child){for(o=a.child;o;){if(o===n)return Xe(a),e;if(o===r)return Xe(a),t;o=o.sibling}throw Error(i(188))}if(n.return!==r.return)n=a,r=o;else{for(var l=!1,s=a.child;s;){if(s===n){l=!0,n=a,r=o;break}if(s===r){l=!0,r=a,n=o;break}s=s.sibling}if(!l){for(s=o.child;s;){if(s===n){l=!0,n=o,r=a;break}if(s===r){l=!0,r=o,n=a;break}s=s.sibling}if(!l)throw Error(i(189))}}if(n.alternate!==r)throw Error(i(190))}if(3!==n.tag)throw Error(i(188));return n.stateNode.current===n?e:t}(e),!e)return null;for(var t=e;;){if(5===t.tag||6===t.tag)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===e)break;for(;!t.sibling;){if(!t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}function et(e,t){for(var n=e.alternate;null!==t;){if(t===e||t===n)return!0;t=t.return}return!1}var tt,nt,rt,at,ot=!1,it=[],lt=null,st=null,ct=null,ut=new Map,dt=new Map,ft=[],pt="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 mt(e,t,n,r,a){return{blockedOn:e,domEventName:t,eventSystemFlags:16|n,nativeEvent:a,targetContainers:[r]}}function ht(e,t){switch(e){case"focusin":case"focusout":lt=null;break;case"dragenter":case"dragleave":st=null;break;case"mouseover":case"mouseout":ct=null;break;case"pointerover":case"pointerout":ut.delete(t.pointerId);break;case"gotpointercapture":case"lostpointercapture":dt.delete(t.pointerId)}}function gt(e,t,n,r,a,o){return null===e||e.nativeEvent!==o?(e=mt(t,n,r,a,o),null!==t&&(null!==(t=ra(t))&&nt(t)),e):(e.eventSystemFlags|=r,t=e.targetContainers,null!==a&&-1===t.indexOf(a)&&t.push(a),e)}function vt(e){var t=na(e.target);if(null!==t){var n=Ke(t);if(null!==n)if(13===(t=n.tag)){if(null!==(t=Qe(n)))return e.blockedOn=t,void at(e.lanePriority,(function(){o.unstable_runWithPriority(e.priority,(function(){rt(n)}))}))}else if(3===t&&n.stateNode.hydrate)return void(e.blockedOn=3===n.tag?n.stateNode.containerInfo:null)}e.blockedOn=null}function bt(e){if(null!==e.blockedOn)return!1;for(var t=e.targetContainers;0<t.length;){var n=Jt(e.domEventName,e.eventSystemFlags,t[0],e.nativeEvent);if(null!==n)return null!==(t=ra(n))&&nt(t),e.blockedOn=n,!1;t.shift()}return!0}function yt(e,t,n){bt(e)&&n.delete(t)}function wt(){for(ot=!1;0<it.length;){var e=it[0];if(null!==e.blockedOn){null!==(e=ra(e.blockedOn))&&tt(e);break}for(var t=e.targetContainers;0<t.length;){var n=Jt(e.domEventName,e.eventSystemFlags,t[0],e.nativeEvent);if(null!==n){e.blockedOn=n;break}t.shift()}null===e.blockedOn&&it.shift()}null!==lt&&bt(lt)&&(lt=null),null!==st&&bt(st)&&(st=null),null!==ct&&bt(ct)&&(ct=null),ut.forEach(yt),dt.forEach(yt)}function kt(e,t){e.blockedOn===t&&(e.blockedOn=null,ot||(ot=!0,o.unstable_scheduleCallback(o.unstable_NormalPriority,wt)))}function Et(e){function t(t){return kt(t,e)}if(0<it.length){kt(it[0],e);for(var n=1;n<it.length;n++){var r=it[n];r.blockedOn===e&&(r.blockedOn=null)}}for(null!==lt&&kt(lt,e),null!==st&&kt(st,e),null!==ct&&kt(ct,e),ut.forEach(t),dt.forEach(t),n=0;n<ft.length;n++)(r=ft[n]).blockedOn===e&&(r.blockedOn=null);for(;0<ft.length&&null===(n=ft[0]).blockedOn;)vt(n),null===n.blockedOn&&ft.shift()}function St(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var xt={animationend:St("Animation","AnimationEnd"),animationiteration:St("Animation","AnimationIteration"),animationstart:St("Animation","AnimationStart"),transitionend:St("Transition","TransitionEnd")},_t={},Ct={};function Tt(e){if(_t[e])return _t[e];if(!xt[e])return e;var t,n=xt[e];for(t in n)if(n.hasOwnProperty(t)&&t in Ct)return _t[e]=n[t];return e}d&&(Ct=document.createElement("div").style,"AnimationEvent"in window||(delete xt.animationend.animation,delete xt.animationiteration.animation,delete xt.animationstart.animation),"TransitionEvent"in window||delete xt.transitionend.transition);var Lt=Tt("animationend"),At=Tt("animationiteration"),Pt=Tt("animationstart"),Rt=Tt("transitionend"),Nt=new Map,Ot=new Map,Dt=["abort","abort",Lt,"animationEnd",At,"animationIteration",Pt,"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",Rt,"transitionEnd","waiting","waiting"];function It(e,t){for(var n=0;n<e.length;n+=2){var r=e[n],a=e[n+1];a="on"+(a[0].toUpperCase()+a.slice(1)),Ot.set(r,t),Nt.set(r,a),c(a,[r])}}(0,o.unstable_now)();var Mt=8;function jt(e){if(0!=(1&e))return Mt=15,1;if(0!=(2&e))return Mt=14,2;if(0!=(4&e))return Mt=13,4;var t=24&e;return 0!==t?(Mt=12,t):0!=(32&e)?(Mt=11,32):0!==(t=192&e)?(Mt=10,t):0!=(256&e)?(Mt=9,256):0!==(t=3584&e)?(Mt=8,t):0!=(4096&e)?(Mt=7,4096):0!==(t=4186112&e)?(Mt=6,t):0!==(t=62914560&e)?(Mt=5,t):67108864&e?(Mt=4,67108864):0!=(134217728&e)?(Mt=3,134217728):0!==(t=805306368&e)?(Mt=2,t):0!=(1073741824&e)?(Mt=1,1073741824):(Mt=8,e)}function Ft(e,t){var n=e.pendingLanes;if(0===n)return Mt=0;var r=0,a=0,o=e.expiredLanes,i=e.suspendedLanes,l=e.pingedLanes;if(0!==o)r=o,a=Mt=15;else if(0!==(o=134217727&n)){var s=o&~i;0!==s?(r=jt(s),a=Mt):0!==(l&=o)&&(r=jt(l),a=Mt)}else 0!==(o=n&~i)?(r=jt(o),a=Mt):0!==l&&(r=jt(l),a=Mt);if(0===r)return 0;if(r=n&((0>(r=31-$t(r))?0:1<<r)<<1)-1,0!==t&&t!==r&&0==(t&i)){if(jt(t),a<=Mt)return t;Mt=a}if(0!==(t=e.entangledLanes))for(e=e.entanglements,t&=r;0<t;)a=1<<(n=31-$t(t)),r|=e[n],t&=~a;return r}function Bt(e){return 0!==(e=-1073741825&e.pendingLanes)?e:1073741824&e?1073741824:0}function zt(e,t){switch(e){case 15:return 1;case 14:return 2;case 12:return 0===(e=Ut(24&~t))?zt(10,t):e;case 10:return 0===(e=Ut(192&~t))?zt(8,t):e;case 8:return 0===(e=Ut(3584&~t))&&(0===(e=Ut(4186112&~t))&&(e=512)),e;case 2:return 0===(t=Ut(805306368&~t))&&(t=268435456),t}throw Error(i(358,e))}function Ut(e){return e&-e}function qt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function Gt(e,t,n){e.pendingLanes|=t;var r=t-1;e.suspendedLanes&=r,e.pingedLanes&=r,(e=e.eventTimes)[t=31-$t(t)]=n}var $t=Math.clz32?Math.clz32:function(e){return 0===e?32:31-(Ht(e)/Zt|0)|0},Ht=Math.log,Zt=Math.LN2;var Vt=o.unstable_UserBlockingPriority,Wt=o.unstable_runWithPriority,Yt=!0;function Kt(e,t,n,r){je||Ie();var a=Xt,o=je;je=!0;try{De(a,e,t,n,r)}finally{(je=o)||Be()}}function Qt(e,t,n,r){Wt(Vt,Xt.bind(null,e,t,n,r))}function Xt(e,t,n,r){var a;if(Yt)if((a=0==(4&t))&&0<it.length&&-1<pt.indexOf(e))e=mt(null,e,t,n,r),it.push(e);else{var o=Jt(e,t,n,r);if(null===o)a&&ht(e,r);else{if(a){if(-1<pt.indexOf(e))return e=mt(o,e,t,n,r),void it.push(e);if(function(e,t,n,r,a){switch(t){case"focusin":return lt=gt(lt,e,t,n,r,a),!0;case"dragenter":return st=gt(st,e,t,n,r,a),!0;case"mouseover":return ct=gt(ct,e,t,n,r,a),!0;case"pointerover":var o=a.pointerId;return ut.set(o,gt(ut.get(o)||null,e,t,n,r,a)),!0;case"gotpointercapture":return o=a.pointerId,dt.set(o,gt(dt.get(o)||null,e,t,n,r,a)),!0}return!1}(o,e,t,n,r))return;ht(e,r)}Ir(e,t,r,null,n)}}}function Jt(e,t,n,r){var a=Ce(r);if(null!==(a=na(a))){var o=Ke(a);if(null===o)a=null;else{var i=o.tag;if(13===i){if(null!==(a=Qe(o)))return a;a=null}else if(3===i){if(o.stateNode.hydrate)return 3===o.tag?o.stateNode.containerInfo:null;a=null}else o!==a&&(a=null)}}return Ir(e,t,r,a,n),null}var en=null,tn=null,nn=null;function rn(){if(nn)return nn;var e,t,n=tn,r=n.length,a="value"in en?en.value:en.textContent,o=a.length;for(e=0;e<r&&n[e]===a[e];e++);var i=r-e;for(t=1;t<=i&&n[r-t]===a[o-t];t++);return nn=a.slice(e,1<t?1-t:void 0)}function an(e){var t=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,10===e&&(e=13),32<=e||13===e?e:0}function on(){return!0}function ln(){return!1}function sn(e){function t(t,n,r,a,o){for(var i in this._reactName=t,this._targetInst=r,this.type=n,this.nativeEvent=a,this.target=o,this.currentTarget=null,e)e.hasOwnProperty(i)&&(t=e[i],this[i]=t?t(a):a[i]);return this.isDefaultPrevented=(null!=a.defaultPrevented?a.defaultPrevented:!1===a.returnValue)?on:ln,this.isPropagationStopped=ln,this}return a(t.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=on)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=on)},persist:function(){},isPersistent:on}),t}var cn,un,dn,fn={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},pn=sn(fn),mn=a({},fn,{view:0,detail:0}),hn=sn(mn),gn=a({},mn,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:Ln,button:0,buttons:0,relatedTarget:function(e){return void 0===e.relatedTarget?e.fromElement===e.srcElement?e.toElement:e.fromElement:e.relatedTarget},movementX:function(e){return"movementX"in e?e.movementX:(e!==dn&&(dn&&"mousemove"===e.type?(cn=e.screenX-dn.screenX,un=e.screenY-dn.screenY):un=cn=0,dn=e),cn)},movementY:function(e){return"movementY"in e?e.movementY:un}}),vn=sn(gn),bn=sn(a({},gn,{dataTransfer:0})),yn=sn(a({},mn,{relatedTarget:0})),wn=sn(a({},fn,{animationName:0,elapsedTime:0,pseudoElement:0})),kn=a({},fn,{clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),En=sn(kn),Sn=sn(a({},fn,{data:0})),xn={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},_n={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"},Cn={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function Tn(e){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(e):!!(e=Cn[e])&&!!t[e]}function Ln(){return Tn}var An=a({},mn,{key:function(e){if(e.key){var t=xn[e.key]||e.key;if("Unidentified"!==t)return t}return"keypress"===e.type?13===(e=an(e))?"Enter":String.fromCharCode(e):"keydown"===e.type||"keyup"===e.type?_n[e.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:Ln,charCode:function(e){return"keypress"===e.type?an(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?an(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}}),Pn=sn(An),Rn=sn(a({},gn,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0})),Nn=sn(a({},mn,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:Ln})),On=sn(a({},fn,{propertyName:0,elapsedTime:0,pseudoElement:0})),Dn=a({},gn,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:0,deltaMode:0}),In=sn(Dn),Mn=[9,13,27,32],jn=d&&"CompositionEvent"in window,Fn=null;d&&"documentMode"in document&&(Fn=document.documentMode);var Bn=d&&"TextEvent"in window&&!Fn,zn=d&&(!jn||Fn&&8<Fn&&11>=Fn),Un=String.fromCharCode(32),qn=!1;function Gn(e,t){switch(e){case"keyup":return-1!==Mn.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function $n(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var Hn=!1;var Zn={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 Vn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!Zn[e.type]:"textarea"===t}function Wn(e,t,n,r){Re(r),0<(t=jr(t,"onChange")).length&&(n=new pn("onChange","change",null,n,r),e.push({event:n,listeners:t}))}var Yn=null,Kn=null;function Qn(e){Ar(e,0)}function Xn(e){if(Q(aa(e)))return e}function Jn(e,t){if("change"===e)return t}var er=!1;if(d){var tr;if(d){var nr="oninput"in document;if(!nr){var rr=document.createElement("div");rr.setAttribute("oninput","return;"),nr="function"==typeof rr.oninput}tr=nr}else tr=!1;er=tr&&(!document.documentMode||9<document.documentMode)}function ar(){Yn&&(Yn.detachEvent("onpropertychange",or),Kn=Yn=null)}function or(e){if("value"===e.propertyName&&Xn(Kn)){var t=[];if(Wn(t,Kn,e,Ce(e)),e=Qn,je)e(t);else{je=!0;try{Oe(e,t)}finally{je=!1,Be()}}}}function ir(e,t,n){"focusin"===e?(ar(),Kn=n,(Yn=t).attachEvent("onpropertychange",or)):"focusout"===e&&ar()}function lr(e){if("selectionchange"===e||"keyup"===e||"keydown"===e)return Xn(Kn)}function sr(e,t){if("click"===e)return Xn(t)}function cr(e,t){if("input"===e||"change"===e)return Xn(t)}var ur="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},dr=Object.prototype.hasOwnProperty;function fr(e,t){if(ur(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++)if(!dr.call(t,n[r])||!ur(e[n[r]],t[n[r]]))return!1;return!0}function pr(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function mr(e,t){var n,r=pr(e);for(e=0;r;){if(3===r.nodeType){if(n=e+r.textContent.length,e<=t&&n>=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=pr(r)}}function hr(e,t){return!(!e||!t)&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?hr(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function gr(){for(var e=window,t=X();t instanceof e.HTMLIFrameElement;){try{var n="string"==typeof t.contentWindow.location.href}catch(r){n=!1}if(!n)break;t=X((e=t.contentWindow).document)}return t}function vr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}var br=d&&"documentMode"in document&&11>=document.documentMode,yr=null,wr=null,kr=null,Er=!1;function Sr(e,t,n){var r=n.window===n?n.document:9===n.nodeType?n:n.ownerDocument;Er||null==yr||yr!==X(r)||("selectionStart"in(r=yr)&&vr(r)?r={start:r.selectionStart,end:r.selectionEnd}:r={anchorNode:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset},kr&&fr(kr,r)||(kr=r,0<(r=jr(wr,"onSelect")).length&&(t=new pn("onSelect","select",null,t,n),e.push({event:t,listeners:r}),t.target=yr)))}It("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),It("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),It(Dt,2);for(var xr="change selectionchange textInput compositionstart compositionend compositionupdate".split(" "),_r=0;_r<xr.length;_r++)Ot.set(xr[_r],0);u("onMouseEnter",["mouseout","mouseover"]),u("onMouseLeave",["mouseout","mouseover"]),u("onPointerEnter",["pointerout","pointerover"]),u("onPointerLeave",["pointerout","pointerover"]),c("onChange","change click focusin focusout input keydown keyup selectionchange".split(" ")),c("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" ")),c("onBeforeInput",["compositionend","keypress","textInput","paste"]),c("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" ")),c("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" ")),c("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var Cr="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Tr=new Set("cancel close invalid load scroll toggle".split(" ").concat(Cr));function Lr(e,t,n){var r=e.type||"unknown-event";e.currentTarget=n,function(e,t,n,r,a,o,l,s,c){if(Ye.apply(this,arguments),$e){if(!$e)throw Error(i(198));var u=He;$e=!1,He=null,Ze||(Ze=!0,Ve=u)}}(r,t,void 0,e),e.currentTarget=null}function Ar(e,t){t=0!=(4&t);for(var n=0;n<e.length;n++){var r=e[n],a=r.event;r=r.listeners;e:{var o=void 0;if(t)for(var i=r.length-1;0<=i;i--){var l=r[i],s=l.instance,c=l.currentTarget;if(l=l.listener,s!==o&&a.isPropagationStopped())break e;Lr(a,l,c),o=s}else for(i=0;i<r.length;i++){if(s=(l=r[i]).instance,c=l.currentTarget,l=l.listener,s!==o&&a.isPropagationStopped())break e;Lr(a,l,c),o=s}}}if(Ze)throw e=Ve,Ze=!1,Ve=null,e}function Pr(e,t){var n=ia(t),r=e+"__bubble";n.has(r)||(Dr(t,e,2,!1),n.add(r))}var Rr="_reactListening"+Math.random().toString(36).slice(2);function Nr(e){e[Rr]||(e[Rr]=!0,l.forEach((function(t){Tr.has(t)||Or(t,!1,e,null),Or(t,!0,e,null)})))}function Or(e,t,n,r){var a=4<arguments.length&&void 0!==arguments[4]?arguments[4]:0,o=n;if("selectionchange"===e&&9!==n.nodeType&&(o=n.ownerDocument),null!==r&&!t&&Tr.has(e)){if("scroll"!==e)return;a|=2,o=r}var i=ia(o),l=e+"__"+(t?"capture":"bubble");i.has(l)||(t&&(a|=4),Dr(o,e,a,t),i.add(l))}function Dr(e,t,n,r){var a=Ot.get(t);switch(void 0===a?2:a){case 0:a=Kt;break;case 1:a=Qt;break;default:a=Xt}n=a.bind(null,t,n,e),a=void 0,!Ue||"touchstart"!==t&&"touchmove"!==t&&"wheel"!==t||(a=!0),r?void 0!==a?e.addEventListener(t,n,{capture:!0,passive:a}):e.addEventListener(t,n,!0):void 0!==a?e.addEventListener(t,n,{passive:a}):e.addEventListener(t,n,!1)}function Ir(e,t,n,r,a){var o=r;if(0==(1&t)&&0==(2&t)&&null!==r)e:for(;;){if(null===r)return;var i=r.tag;if(3===i||4===i){var l=r.stateNode.containerInfo;if(l===a||8===l.nodeType&&l.parentNode===a)break;if(4===i)for(i=r.return;null!==i;){var s=i.tag;if((3===s||4===s)&&((s=i.stateNode.containerInfo)===a||8===s.nodeType&&s.parentNode===a))return;i=i.return}for(;null!==l;){if(null===(i=na(l)))return;if(5===(s=i.tag)||6===s){r=o=i;continue e}l=l.parentNode}}r=r.return}!function(e,t,n){if(Fe)return e(t,n);Fe=!0;try{Me(e,t,n)}finally{Fe=!1,Be()}}((function(){var r=o,a=Ce(n),i=[];e:{var l=Nt.get(e);if(void 0!==l){var s=pn,c=e;switch(e){case"keypress":if(0===an(n))break e;case"keydown":case"keyup":s=Pn;break;case"focusin":c="focus",s=yn;break;case"focusout":c="blur",s=yn;break;case"beforeblur":case"afterblur":s=yn;break;case"click":if(2===n.button)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":s=vn;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":s=bn;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":s=Nn;break;case Lt:case At:case Pt:s=wn;break;case Rt:s=On;break;case"scroll":s=hn;break;case"wheel":s=In;break;case"copy":case"cut":case"paste":s=En;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":s=Rn}var u=0!=(4&t),d=!u&&"scroll"===e,f=u?null!==l?l+"Capture":null:l;u=[];for(var p,m=r;null!==m;){var h=(p=m).stateNode;if(5===p.tag&&null!==h&&(p=h,null!==f&&(null!=(h=ze(m,f))&&u.push(Mr(m,h,p)))),d)break;m=m.return}0<u.length&&(l=new s(l,c,null,n,a),i.push({event:l,listeners:u}))}}if(0==(7&t)){if(s="mouseout"===e||"pointerout"===e,(!(l="mouseover"===e||"pointerover"===e)||0!=(16&t)||!(c=n.relatedTarget||n.fromElement)||!na(c)&&!c[ea])&&(s||l)&&(l=a.window===a?a:(l=a.ownerDocument)?l.defaultView||l.parentWindow:window,s?(s=r,null!==(c=(c=n.relatedTarget||n.toElement)?na(c):null)&&(c!==(d=Ke(c))||5!==c.tag&&6!==c.tag)&&(c=null)):(s=null,c=r),s!==c)){if(u=vn,h="onMouseLeave",f="onMouseEnter",m="mouse","pointerout"!==e&&"pointerover"!==e||(u=Rn,h="onPointerLeave",f="onPointerEnter",m="pointer"),d=null==s?l:aa(s),p=null==c?l:aa(c),(l=new u(h,m+"leave",s,n,a)).target=d,l.relatedTarget=p,h=null,na(a)===r&&((u=new u(f,m+"enter",c,n,a)).target=p,u.relatedTarget=d,h=u),d=h,s&&c)e:{for(f=c,m=0,p=u=s;p;p=Fr(p))m++;for(p=0,h=f;h;h=Fr(h))p++;for(;0<m-p;)u=Fr(u),m--;for(;0<p-m;)f=Fr(f),p--;for(;m--;){if(u===f||null!==f&&u===f.alternate)break e;u=Fr(u),f=Fr(f)}u=null}else u=null;null!==s&&Br(i,l,s,u,!1),null!==c&&null!==d&&Br(i,d,c,u,!0)}if("select"===(s=(l=r?aa(r):window).nodeName&&l.nodeName.toLowerCase())||"input"===s&&"file"===l.type)var g=Jn;else if(Vn(l))if(er)g=cr;else{g=lr;var v=ir}else(s=l.nodeName)&&"input"===s.toLowerCase()&&("checkbox"===l.type||"radio"===l.type)&&(g=sr);switch(g&&(g=g(e,r))?Wn(i,g,n,a):(v&&v(e,l,r),"focusout"===e&&(v=l._wrapperState)&&v.controlled&&"number"===l.type&&ae(l,"number",l.value)),v=r?aa(r):window,e){case"focusin":(Vn(v)||"true"===v.contentEditable)&&(yr=v,wr=r,kr=null);break;case"focusout":kr=wr=yr=null;break;case"mousedown":Er=!0;break;case"contextmenu":case"mouseup":case"dragend":Er=!1,Sr(i,n,a);break;case"selectionchange":if(br)break;case"keydown":case"keyup":Sr(i,n,a)}var b;if(jn)e:{switch(e){case"compositionstart":var y="onCompositionStart";break e;case"compositionend":y="onCompositionEnd";break e;case"compositionupdate":y="onCompositionUpdate";break e}y=void 0}else Hn?Gn(e,n)&&(y="onCompositionEnd"):"keydown"===e&&229===n.keyCode&&(y="onCompositionStart");y&&(zn&&"ko"!==n.locale&&(Hn||"onCompositionStart"!==y?"onCompositionEnd"===y&&Hn&&(b=rn()):(tn="value"in(en=a)?en.value:en.textContent,Hn=!0)),0<(v=jr(r,y)).length&&(y=new Sn(y,e,null,n,a),i.push({event:y,listeners:v}),b?y.data=b:null!==(b=$n(n))&&(y.data=b))),(b=Bn?function(e,t){switch(e){case"compositionend":return $n(t);case"keypress":return 32!==t.which?null:(qn=!0,Un);case"textInput":return(e=t.data)===Un&&qn?null:e;default:return null}}(e,n):function(e,t){if(Hn)return"compositionend"===e||!jn&&Gn(e,t)?(e=rn(),nn=tn=en=null,Hn=!1,e):null;switch(e){case"paste":default:return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1<t.char.length)return t.char;if(t.which)return String.fromCharCode(t.which)}return null;case"compositionend":return zn&&"ko"!==t.locale?null:t.data}}(e,n))&&(0<(r=jr(r,"onBeforeInput")).length&&(a=new Sn("onBeforeInput","beforeinput",null,n,a),i.push({event:a,listeners:r}),a.data=b))}Ar(i,t)}))}function Mr(e,t,n){return{instance:e,listener:t,currentTarget:n}}function jr(e,t){for(var n=t+"Capture",r=[];null!==e;){var a=e,o=a.stateNode;5===a.tag&&null!==o&&(a=o,null!=(o=ze(e,n))&&r.unshift(Mr(e,o,a)),null!=(o=ze(e,t))&&r.push(Mr(e,o,a))),e=e.return}return r}function Fr(e){if(null===e)return null;do{e=e.return}while(e&&5!==e.tag);return e||null}function Br(e,t,n,r,a){for(var o=t._reactName,i=[];null!==n&&n!==r;){var l=n,s=l.alternate,c=l.stateNode;if(null!==s&&s===r)break;5===l.tag&&null!==c&&(l=c,a?null!=(s=ze(n,o))&&i.unshift(Mr(n,s,l)):a||null!=(s=ze(n,o))&&i.push(Mr(n,s,l))),n=n.return}0!==i.length&&e.push({event:t,listeners:i})}function zr(){}var Ur=null,qr=null;function Gr(e,t){switch(e){case"button":case"input":case"select":case"textarea":return!!t.autoFocus}return!1}function $r(e,t){return"textarea"===e||"option"===e||"noscript"===e||"string"==typeof t.children||"number"==typeof t.children||"object"==typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var Hr="function"==typeof setTimeout?setTimeout:void 0,Zr="function"==typeof clearTimeout?clearTimeout:void 0;function Vr(e){1===e.nodeType?e.textContent="":9===e.nodeType&&(null!=(e=e.body)&&(e.textContent=""))}function Wr(e){for(;null!=e;e=e.nextSibling){var t=e.nodeType;if(1===t||3===t)break}return e}function Yr(e){e=e.previousSibling;for(var t=0;e;){if(8===e.nodeType){var n=e.data;if("$"===n||"$!"===n||"$?"===n){if(0===t)return e;t--}else"/$"===n&&t++}e=e.previousSibling}return null}var Kr=0;var Qr=Math.random().toString(36).slice(2),Xr="__reactFiber$"+Qr,Jr="__reactProps$"+Qr,ea="__reactContainer$"+Qr,ta="__reactEvents$"+Qr;function na(e){var t=e[Xr];if(t)return t;for(var n=e.parentNode;n;){if(t=n[ea]||n[Xr]){if(n=t.alternate,null!==t.child||null!==n&&null!==n.child)for(e=Yr(e);null!==e;){if(n=e[Xr])return n;e=Yr(e)}return t}n=(e=n).parentNode}return null}function ra(e){return!(e=e[Xr]||e[ea])||5!==e.tag&&6!==e.tag&&13!==e.tag&&3!==e.tag?null:e}function aa(e){if(5===e.tag||6===e.tag)return e.stateNode;throw Error(i(33))}function oa(e){return e[Jr]||null}function ia(e){var t=e[ta];return void 0===t&&(t=e[ta]=new Set),t}var la=[],sa=-1;function ca(e){return{current:e}}function ua(e){0>sa||(e.current=la[sa],la[sa]=null,sa--)}function da(e,t){sa++,la[sa]=e.current,e.current=t}var fa={},pa=ca(fa),ma=ca(!1),ha=fa;function ga(e,t){var n=e.type.contextTypes;if(!n)return fa;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var a,o={};for(a in n)o[a]=t[a];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function va(e){return null!=(e=e.childContextTypes)}function ba(){ua(ma),ua(pa)}function ya(e,t,n){if(pa.current!==fa)throw Error(i(168));da(pa,t),da(ma,n)}function wa(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var o in r=r.getChildContext())if(!(o in e))throw Error(i(108,V(t)||"Unknown",o));return a({},n,r)}function ka(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||fa,ha=pa.current,da(pa,e),da(ma,ma.current),!0}function Ea(e,t,n){var r=e.stateNode;if(!r)throw Error(i(169));n?(e=wa(e,t,ha),r.__reactInternalMemoizedMergedChildContext=e,ua(ma),ua(pa),da(pa,e)):ua(ma),da(ma,n)}var Sa=null,xa=null,_a=o.unstable_runWithPriority,Ca=o.unstable_scheduleCallback,Ta=o.unstable_cancelCallback,La=o.unstable_shouldYield,Aa=o.unstable_requestPaint,Pa=o.unstable_now,Ra=o.unstable_getCurrentPriorityLevel,Na=o.unstable_ImmediatePriority,Oa=o.unstable_UserBlockingPriority,Da=o.unstable_NormalPriority,Ia=o.unstable_LowPriority,Ma=o.unstable_IdlePriority,ja={},Fa=void 0!==Aa?Aa:function(){},Ba=null,za=null,Ua=!1,qa=Pa(),Ga=1e4>qa?Pa:function(){return Pa()-qa};function $a(){switch(Ra()){case Na:return 99;case Oa:return 98;case Da:return 97;case Ia:return 96;case Ma:return 95;default:throw Error(i(332))}}function Ha(e){switch(e){case 99:return Na;case 98:return Oa;case 97:return Da;case 96:return Ia;case 95:return Ma;default:throw Error(i(332))}}function Za(e,t){return e=Ha(e),_a(e,t)}function Va(e,t,n){return e=Ha(e),Ca(e,t,n)}function Wa(){if(null!==za){var e=za;za=null,Ta(e)}Ya()}function Ya(){if(!Ua&&null!==Ba){Ua=!0;var e=0;try{var t=Ba;Za(99,(function(){for(;e<t.length;e++){var n=t[e];do{n=n(!0)}while(null!==n)}})),Ba=null}catch(n){throw null!==Ba&&(Ba=Ba.slice(e+1)),Ca(Na,Wa),n}finally{Ua=!1}}}var Ka=k.ReactCurrentBatchConfig;function Qa(e,t){if(e&&e.defaultProps){for(var n in t=a({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}return t}var Xa=ca(null),Ja=null,eo=null,to=null;function no(){to=eo=Ja=null}function ro(e){var t=Xa.current;ua(Xa),e.type._context._currentValue=t}function ao(e,t){for(;null!==e;){var n=e.alternate;if((e.childLanes&t)===t){if(null===n||(n.childLanes&t)===t)break;n.childLanes|=t}else e.childLanes|=t,null!==n&&(n.childLanes|=t);e=e.return}}function oo(e,t){Ja=e,to=eo=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(0!=(e.lanes&t)&&(ji=!0),e.firstContext=null)}function io(e,t){if(to!==e&&!1!==t&&0!==t)if("number"==typeof t&&1073741823!==t||(to=e,t=1073741823),t={context:e,observedBits:t,next:null},null===eo){if(null===Ja)throw Error(i(308));eo=t,Ja.dependencies={lanes:0,firstContext:t,responders:null}}else eo=eo.next=t;return e._currentValue}var lo=!1;function so(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null},effects:null}}function co(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function uo(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function fo(e,t){if(null!==(e=e.updateQueue)){var n=(e=e.shared).pending;null===n?t.next=t:(t.next=n.next,n.next=t),e.pending=t}}function po(e,t){var n=e.updateQueue,r=e.alternate;if(null!==r&&n===(r=r.updateQueue)){var a=null,o=null;if(null!==(n=n.firstBaseUpdate)){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};null===o?a=o=i:o=o.next=i,n=n.next}while(null!==n);null===o?a=o=t:o=o.next=t}else a=o=t;return n={baseState:r.baseState,firstBaseUpdate:a,lastBaseUpdate:o,shared:r.shared,effects:r.effects},void(e.updateQueue=n)}null===(e=n.lastBaseUpdate)?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function mo(e,t,n,r){var o=e.updateQueue;lo=!1;var i=o.firstBaseUpdate,l=o.lastBaseUpdate,s=o.shared.pending;if(null!==s){o.shared.pending=null;var c=s,u=c.next;c.next=null,null===l?i=u:l.next=u,l=c;var d=e.alternate;if(null!==d){var f=(d=d.updateQueue).lastBaseUpdate;f!==l&&(null===f?d.firstBaseUpdate=u:f.next=u,d.lastBaseUpdate=c)}}if(null!==i){for(f=o.baseState,l=0,d=u=c=null;;){s=i.lane;var p=i.eventTime;if((r&s)===s){null!==d&&(d=d.next={eventTime:p,lane:0,tag:i.tag,payload:i.payload,callback:i.callback,next:null});e:{var m=e,h=i;switch(s=t,p=n,h.tag){case 1:if("function"==typeof(m=h.payload)){f=m.call(p,f,s);break e}f=m;break e;case 3:m.flags=-4097&m.flags|64;case 0:if(null==(s="function"==typeof(m=h.payload)?m.call(p,f,s):m))break e;f=a({},f,s);break e;case 2:lo=!0}}null!==i.callback&&(e.flags|=32,null===(s=o.effects)?o.effects=[i]:s.push(i))}else p={eventTime:p,lane:s,tag:i.tag,payload:i.payload,callback:i.callback,next:null},null===d?(u=d=p,c=f):d=d.next=p,l|=s;if(null===(i=i.next)){if(null===(s=o.shared.pending))break;i=s.next,s.next=null,o.lastBaseUpdate=s,o.shared.pending=null}}null===d&&(c=f),o.baseState=c,o.firstBaseUpdate=u,o.lastBaseUpdate=d,Ul|=l,e.lanes=l,e.memoizedState=f}}function ho(e,t,n){if(e=t.effects,t.effects=null,null!==e)for(t=0;t<e.length;t++){var r=e[t],a=r.callback;if(null!==a){if(r.callback=null,r=n,"function"!=typeof a)throw Error(i(191,a));a.call(r)}}}var go=(new r.Component).refs;function vo(e,t,n,r){n=null==(n=n(r,t=e.memoizedState))?t:a({},t,n),e.memoizedState=n,0===e.lanes&&(e.updateQueue.baseState=n)}var bo={isMounted:function(e){return!!(e=e._reactInternals)&&Ke(e)===e},enqueueSetState:function(e,t,n){e=e._reactInternals;var r=fs(),a=ps(e),o=uo(r,a);o.payload=t,null!=n&&(o.callback=n),fo(e,o),ms(e,a,r)},enqueueReplaceState:function(e,t,n){e=e._reactInternals;var r=fs(),a=ps(e),o=uo(r,a);o.tag=1,o.payload=t,null!=n&&(o.callback=n),fo(e,o),ms(e,a,r)},enqueueForceUpdate:function(e,t){e=e._reactInternals;var n=fs(),r=ps(e),a=uo(n,r);a.tag=2,null!=t&&(a.callback=t),fo(e,a),ms(e,r,n)}};function yo(e,t,n,r,a,o,i){return"function"==typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,o,i):!t.prototype||!t.prototype.isPureReactComponent||(!fr(n,r)||!fr(a,o))}function wo(e,t,n){var r=!1,a=fa,o=t.contextType;return"object"==typeof o&&null!==o?o=io(o):(a=va(t)?ha:pa.current,o=(r=null!=(r=t.contextTypes))?ga(e,a):fa),t=new t(n,o),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=bo,e.stateNode=t,t._reactInternals=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=a,e.__reactInternalMemoizedMaskedChildContext=o),t}function ko(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&bo.enqueueReplaceState(t,t.state,null)}function Eo(e,t,n,r){var a=e.stateNode;a.props=n,a.state=e.memoizedState,a.refs=go,so(e);var o=t.contextType;"object"==typeof o&&null!==o?a.context=io(o):(o=va(t)?ha:pa.current,a.context=ga(e,o)),mo(e,n,a,r),a.state=e.memoizedState,"function"==typeof(o=t.getDerivedStateFromProps)&&(vo(e,t,o,n),a.state=e.memoizedState),"function"==typeof t.getDerivedStateFromProps||"function"==typeof a.getSnapshotBeforeUpdate||"function"!=typeof a.UNSAFE_componentWillMount&&"function"!=typeof a.componentWillMount||(t=a.state,"function"==typeof a.componentWillMount&&a.componentWillMount(),"function"==typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount(),t!==a.state&&bo.enqueueReplaceState(a,a.state,null),mo(e,n,a,r),a.state=e.memoizedState),"function"==typeof a.componentDidMount&&(e.flags|=4)}var So=Array.isArray;function xo(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){if(n=n._owner){if(1!==n.tag)throw Error(i(309));var r=n.stateNode}if(!r)throw Error(i(147,e));var a=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===a?t.ref:(t=function(e){var t=r.refs;t===go&&(t=r.refs={}),null===e?delete t[a]:t[a]=e},t._stringRef=a,t)}if("string"!=typeof e)throw Error(i(284));if(!n._owner)throw Error(i(290,e))}return e}function _o(e,t){if("textarea"!==e.type)throw Error(i(31,"[object Object]"===Object.prototype.toString.call(t)?"object with keys {"+Object.keys(t).join(", ")+"}":t))}function Co(e){function t(t,n){if(e){var r=t.lastEffect;null!==r?(r.nextEffect=n,t.lastEffect=n):t.firstEffect=t.lastEffect=n,n.nextEffect=null,n.flags=8}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function a(e,t){return(e=Zs(e,t)).index=0,e.sibling=null,e}function o(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.flags=2,n):r:(t.flags=2,n):n}function l(t){return e&&null===t.alternate&&(t.flags=2),t}function s(e,t,n,r){return null===t||6!==t.tag?((t=Ks(n,e.mode,r)).return=e,t):((t=a(t,n)).return=e,t)}function c(e,t,n,r){return null!==t&&t.elementType===n.type?((r=a(t,n.props)).ref=xo(e,t,n),r.return=e,r):((r=Vs(n.type,n.key,n.props,null,e.mode,r)).ref=xo(e,t,n),r.return=e,r)}function u(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=Qs(n,e.mode,r)).return=e,t):((t=a(t,n.children||[])).return=e,t)}function d(e,t,n,r,o){return null===t||7!==t.tag?((t=Ws(n,e.mode,r,o)).return=e,t):((t=a(t,n)).return=e,t)}function f(e,t,n){if("string"==typeof t||"number"==typeof t)return(t=Ks(""+t,e.mode,n)).return=e,t;if("object"==typeof t&&null!==t){switch(t.$$typeof){case E:return(n=Vs(t.type,t.key,t.props,null,e.mode,n)).ref=xo(e,null,t),n.return=e,n;case S:return(t=Qs(t,e.mode,n)).return=e,t}if(So(t)||q(t))return(t=Ws(t,e.mode,n,null)).return=e,t;_o(e,t)}return null}function p(e,t,n,r){var a=null!==t?t.key:null;if("string"==typeof n||"number"==typeof n)return null!==a?null:s(e,t,""+n,r);if("object"==typeof n&&null!==n){switch(n.$$typeof){case E:return n.key===a?n.type===x?d(e,t,n.props.children,r,a):c(e,t,n,r):null;case S:return n.key===a?u(e,t,n,r):null}if(So(n)||q(n))return null!==a?null:d(e,t,n,r,null);_o(e,n)}return null}function m(e,t,n,r,a){if("string"==typeof r||"number"==typeof r)return s(t,e=e.get(n)||null,""+r,a);if("object"==typeof r&&null!==r){switch(r.$$typeof){case E:return e=e.get(null===r.key?n:r.key)||null,r.type===x?d(t,e,r.props.children,a,r.key):c(t,e,r,a);case S:return u(t,e=e.get(null===r.key?n:r.key)||null,r,a)}if(So(r)||q(r))return d(t,e=e.get(n)||null,r,a,null);_o(t,r)}return null}function h(a,i,l,s){for(var c=null,u=null,d=i,h=i=0,g=null;null!==d&&h<l.length;h++){d.index>h?(g=d,d=null):g=d.sibling;var v=p(a,d,l[h],s);if(null===v){null===d&&(d=g);break}e&&d&&null===v.alternate&&t(a,d),i=o(v,i,h),null===u?c=v:u.sibling=v,u=v,d=g}if(h===l.length)return n(a,d),c;if(null===d){for(;h<l.length;h++)null!==(d=f(a,l[h],s))&&(i=o(d,i,h),null===u?c=d:u.sibling=d,u=d);return c}for(d=r(a,d);h<l.length;h++)null!==(g=m(d,a,h,l[h],s))&&(e&&null!==g.alternate&&d.delete(null===g.key?h:g.key),i=o(g,i,h),null===u?c=g:u.sibling=g,u=g);return e&&d.forEach((function(e){return t(a,e)})),c}function g(a,l,s,c){var u=q(s);if("function"!=typeof u)throw Error(i(150));if(null==(s=u.call(s)))throw Error(i(151));for(var d=u=null,h=l,g=l=0,v=null,b=s.next();null!==h&&!b.done;g++,b=s.next()){h.index>g?(v=h,h=null):v=h.sibling;var y=p(a,h,b.value,c);if(null===y){null===h&&(h=v);break}e&&h&&null===y.alternate&&t(a,h),l=o(y,l,g),null===d?u=y:d.sibling=y,d=y,h=v}if(b.done)return n(a,h),u;if(null===h){for(;!b.done;g++,b=s.next())null!==(b=f(a,b.value,c))&&(l=o(b,l,g),null===d?u=b:d.sibling=b,d=b);return u}for(h=r(a,h);!b.done;g++,b=s.next())null!==(b=m(h,a,g,b.value,c))&&(e&&null!==b.alternate&&h.delete(null===b.key?g:b.key),l=o(b,l,g),null===d?u=b:d.sibling=b,d=b);return e&&h.forEach((function(e){return t(a,e)})),u}return function(e,r,o,s){var c="object"==typeof o&&null!==o&&o.type===x&&null===o.key;c&&(o=o.props.children);var u="object"==typeof o&&null!==o;if(u)switch(o.$$typeof){case E:e:{for(u=o.key,c=r;null!==c;){if(c.key===u){if(7===c.tag){if(o.type===x){n(e,c.sibling),(r=a(c,o.props.children)).return=e,e=r;break e}}else if(c.elementType===o.type){n(e,c.sibling),(r=a(c,o.props)).ref=xo(e,c,o),r.return=e,e=r;break e}n(e,c);break}t(e,c),c=c.sibling}o.type===x?((r=Ws(o.props.children,e.mode,s,o.key)).return=e,e=r):((s=Vs(o.type,o.key,o.props,null,e.mode,s)).ref=xo(e,r,o),s.return=e,e=s)}return l(e);case S:e:{for(c=o.key;null!==r;){if(r.key===c){if(4===r.tag&&r.stateNode.containerInfo===o.containerInfo&&r.stateNode.implementation===o.implementation){n(e,r.sibling),(r=a(r,o.children||[])).return=e,e=r;break e}n(e,r);break}t(e,r),r=r.sibling}(r=Qs(o,e.mode,s)).return=e,e=r}return l(e)}if("string"==typeof o||"number"==typeof o)return o=""+o,null!==r&&6===r.tag?(n(e,r.sibling),(r=a(r,o)).return=e,e=r):(n(e,r),(r=Ks(o,e.mode,s)).return=e,e=r),l(e);if(So(o))return h(e,r,o,s);if(q(o))return g(e,r,o,s);if(u&&_o(e,o),void 0===o&&!c)switch(e.tag){case 1:case 22:case 0:case 11:case 15:throw Error(i(152,V(e.type)||"Component"))}return n(e,r)}}var To=Co(!0),Lo=Co(!1),Ao={},Po=ca(Ao),Ro=ca(Ao),No=ca(Ao);function Oo(e){if(e===Ao)throw Error(i(174));return e}function Do(e,t){switch(da(No,t),da(Ro,e),da(Po,Ao),e=t.nodeType){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:me(null,"");break;default:t=me(t=(e=8===e?t.parentNode:t).namespaceURI||null,e=e.tagName)}ua(Po),da(Po,t)}function Io(){ua(Po),ua(Ro),ua(No)}function Mo(e){Oo(No.current);var t=Oo(Po.current),n=me(t,e.type);t!==n&&(da(Ro,e),da(Po,n))}function jo(e){Ro.current===e&&(ua(Po),ua(Ro))}var Fo=ca(0);function Bo(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||"$?"===n.data||"$!"===n.data))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(0!=(64&t.flags))return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var zo=null,Uo=null,qo=!1;function Go(e,t){var n=$s(5,null,null,0);n.elementType="DELETED",n.type="DELETED",n.stateNode=t,n.return=e,n.flags=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function $o(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,!0);case 6:return null!==(t=""===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,!0);default:return!1}}function Ho(e){if(qo){var t=Uo;if(t){var n=t;if(!$o(e,t)){if(!(t=Wr(n.nextSibling))||!$o(e,t))return e.flags=-1025&e.flags|2,qo=!1,void(zo=e);Go(zo,n)}zo=e,Uo=Wr(t.firstChild)}else e.flags=-1025&e.flags|2,qo=!1,zo=e}}function Zo(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;zo=e}function Vo(e){if(e!==zo)return!1;if(!qo)return Zo(e),qo=!0,!1;var t=e.type;if(5!==e.tag||"head"!==t&&"body"!==t&&!$r(t,e.memoizedProps))for(t=Uo;t;)Go(e,t),t=Wr(t.nextSibling);if(Zo(e),13===e.tag){if(!(e=null!==(e=e.memoizedState)?e.dehydrated:null))throw Error(i(317));e:{for(e=e.nextSibling,t=0;e;){if(8===e.nodeType){var n=e.data;if("/$"===n){if(0===t){Uo=Wr(e.nextSibling);break e}t--}else"$"!==n&&"$!"!==n&&"$?"!==n||t++}e=e.nextSibling}Uo=null}}else Uo=zo?Wr(e.stateNode.nextSibling):null;return!0}function Wo(){Uo=zo=null,qo=!1}var Yo=[];function Ko(){for(var e=0;e<Yo.length;e++)Yo[e]._workInProgressVersionPrimary=null;Yo.length=0}var Qo=k.ReactCurrentDispatcher,Xo=k.ReactCurrentBatchConfig,Jo=0,ei=null,ti=null,ni=null,ri=!1,ai=!1;function oi(){throw Error(i(321))}function ii(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!ur(e[n],t[n]))return!1;return!0}function li(e,t,n,r,a,o){if(Jo=o,ei=t,t.memoizedState=null,t.updateQueue=null,t.lanes=0,Qo.current=null===e||null===e.memoizedState?Oi:Di,e=n(r,a),ai){o=0;do{if(ai=!1,!(25>o))throw Error(i(301));o+=1,ni=ti=null,t.updateQueue=null,Qo.current=Ii,e=n(r,a)}while(ai)}if(Qo.current=Ni,t=null!==ti&&null!==ti.next,Jo=0,ni=ti=ei=null,ri=!1,t)throw Error(i(300));return e}function si(){var e={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return null===ni?ei.memoizedState=ni=e:ni=ni.next=e,ni}function ci(){if(null===ti){var e=ei.alternate;e=null!==e?e.memoizedState:null}else e=ti.next;var t=null===ni?ei.memoizedState:ni.next;if(null!==t)ni=t,ti=e;else{if(null===e)throw Error(i(310));e={memoizedState:(ti=e).memoizedState,baseState:ti.baseState,baseQueue:ti.baseQueue,queue:ti.queue,next:null},null===ni?ei.memoizedState=ni=e:ni=ni.next=e}return ni}function ui(e,t){return"function"==typeof t?t(e):t}function di(e){var t=ci(),n=t.queue;if(null===n)throw Error(i(311));n.lastRenderedReducer=e;var r=ti,a=r.baseQueue,o=n.pending;if(null!==o){if(null!==a){var l=a.next;a.next=o.next,o.next=l}r.baseQueue=a=o,n.pending=null}if(null!==a){a=a.next,r=r.baseState;var s=l=o=null,c=a;do{var u=c.lane;if((Jo&u)===u)null!==s&&(s=s.next={lane:0,action:c.action,eagerReducer:c.eagerReducer,eagerState:c.eagerState,next:null}),r=c.eagerReducer===e?c.eagerState:e(r,c.action);else{var d={lane:u,action:c.action,eagerReducer:c.eagerReducer,eagerState:c.eagerState,next:null};null===s?(l=s=d,o=r):s=s.next=d,ei.lanes|=u,Ul|=u}c=c.next}while(null!==c&&c!==a);null===s?o=r:s.next=l,ur(r,t.memoizedState)||(ji=!0),t.memoizedState=r,t.baseState=o,t.baseQueue=s,n.lastRenderedState=r}return[t.memoizedState,n.dispatch]}function fi(e){var t=ci(),n=t.queue;if(null===n)throw Error(i(311));n.lastRenderedReducer=e;var r=n.dispatch,a=n.pending,o=t.memoizedState;if(null!==a){n.pending=null;var l=a=a.next;do{o=e(o,l.action),l=l.next}while(l!==a);ur(o,t.memoizedState)||(ji=!0),t.memoizedState=o,null===t.baseQueue&&(t.baseState=o),n.lastRenderedState=o}return[o,r]}function pi(e,t,n){var r=t._getVersion;r=r(t._source);var a=t._workInProgressVersionPrimary;if(null!==a?e=a===r:(e=e.mutableReadLanes,(e=(Jo&e)===e)&&(t._workInProgressVersionPrimary=r,Yo.push(t))),e)return n(t._source);throw Yo.push(t),Error(i(350))}function mi(e,t,n,r){var a=Ol;if(null===a)throw Error(i(349));var o=t._getVersion,l=o(t._source),s=Qo.current,c=s.useState((function(){return pi(a,t,n)})),u=c[1],d=c[0];c=ni;var f=e.memoizedState,p=f.refs,m=p.getSnapshot,h=f.source;f=f.subscribe;var g=ei;return e.memoizedState={refs:p,source:t,subscribe:r},s.useEffect((function(){p.getSnapshot=n,p.setSnapshot=u;var e=o(t._source);if(!ur(l,e)){e=n(t._source),ur(d,e)||(u(e),e=ps(g),a.mutableReadLanes|=e&a.pendingLanes),e=a.mutableReadLanes,a.entangledLanes|=e;for(var r=a.entanglements,i=e;0<i;){var s=31-$t(i),c=1<<s;r[s]|=e,i&=~c}}}),[n,t,r]),s.useEffect((function(){return r(t._source,(function(){var e=p.getSnapshot,n=p.setSnapshot;try{n(e(t._source));var r=ps(g);a.mutableReadLanes|=r&a.pendingLanes}catch(o){n((function(){throw o}))}}))}),[t,r]),ur(m,n)&&ur(h,t)&&ur(f,r)||((e={pending:null,dispatch:null,lastRenderedReducer:ui,lastRenderedState:d}).dispatch=u=Ri.bind(null,ei,e),c.queue=e,c.baseQueue=null,d=pi(a,t,n),c.memoizedState=c.baseState=d),d}function hi(e,t,n){return mi(ci(),e,t,n)}function gi(e){var t=si();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={pending:null,dispatch:null,lastRenderedReducer:ui,lastRenderedState:e}).dispatch=Ri.bind(null,ei,e),[t.memoizedState,e]}function vi(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===(t=ei.updateQueue)?(t={lastEffect:null},ei.updateQueue=t,t.lastEffect=e.next=e):null===(n=t.lastEffect)?t.lastEffect=e.next=e:(r=n.next,n.next=e,e.next=r,t.lastEffect=e),e}function bi(e){return e={current:e},si().memoizedState=e}function yi(){return ci().memoizedState}function wi(e,t,n,r){var a=si();ei.flags|=e,a.memoizedState=vi(1|t,n,void 0,void 0===r?null:r)}function ki(e,t,n,r){var a=ci();r=void 0===r?null:r;var o=void 0;if(null!==ti){var i=ti.memoizedState;if(o=i.destroy,null!==r&&ii(r,i.deps))return void vi(t,n,o,r)}ei.flags|=e,a.memoizedState=vi(1|t,n,o,r)}function Ei(e,t){return wi(516,4,e,t)}function Si(e,t){return ki(516,4,e,t)}function xi(e,t){return ki(4,2,e,t)}function _i(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!=t?(e=e(),t.current=e,function(){t.current=null}):void 0}function Ci(e,t,n){return n=null!=n?n.concat([e]):null,ki(4,2,_i.bind(null,t,e),n)}function Ti(){}function Li(e,t){var n=ci();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&ii(t,r[1])?r[0]:(n.memoizedState=[e,t],e)}function Ai(e,t){var n=ci();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&ii(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)}function Pi(e,t){var n=$a();Za(98>n?98:n,(function(){e(!0)})),Za(97<n?97:n,(function(){var n=Xo.transition;Xo.transition=1;try{e(!1),t()}finally{Xo.transition=n}}))}function Ri(e,t,n){var r=fs(),a=ps(e),o={lane:a,action:n,eagerReducer:null,eagerState:null,next:null},i=t.pending;if(null===i?o.next=o:(o.next=i.next,i.next=o),t.pending=o,i=e.alternate,e===ei||null!==i&&i===ei)ai=ri=!0;else{if(0===e.lanes&&(null===i||0===i.lanes)&&null!==(i=t.lastRenderedReducer))try{var l=t.lastRenderedState,s=i(l,n);if(o.eagerReducer=i,o.eagerState=s,ur(s,l))return}catch(c){}ms(e,a,r)}}var Ni={readContext:io,useCallback:oi,useContext:oi,useEffect:oi,useImperativeHandle:oi,useLayoutEffect:oi,useMemo:oi,useReducer:oi,useRef:oi,useState:oi,useDebugValue:oi,useDeferredValue:oi,useTransition:oi,useMutableSource:oi,useOpaqueIdentifier:oi,unstable_isNewReconciler:!1},Oi={readContext:io,useCallback:function(e,t){return si().memoizedState=[e,void 0===t?null:t],e},useContext:io,useEffect:Ei,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,wi(4,2,_i.bind(null,t,e),n)},useLayoutEffect:function(e,t){return wi(4,2,e,t)},useMemo:function(e,t){var n=si();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=si();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={pending:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=Ri.bind(null,ei,e),[r.memoizedState,e]},useRef:bi,useState:gi,useDebugValue:Ti,useDeferredValue:function(e){var t=gi(e),n=t[0],r=t[1];return Ei((function(){var t=Xo.transition;Xo.transition=1;try{r(e)}finally{Xo.transition=t}}),[e]),n},useTransition:function(){var e=gi(!1),t=e[0];return bi(e=Pi.bind(null,e[1])),[e,t]},useMutableSource:function(e,t,n){var r=si();return r.memoizedState={refs:{getSnapshot:t,setSnapshot:null},source:e,subscribe:n},mi(r,e,t,n)},useOpaqueIdentifier:function(){if(qo){var e=!1,t=function(e){return{$$typeof:I,toString:e,valueOf:e}}((function(){throw e||(e=!0,n("r:"+(Kr++).toString(36))),Error(i(355))})),n=gi(t)[1];return 0==(2&ei.mode)&&(ei.flags|=516,vi(5,(function(){n("r:"+(Kr++).toString(36))}),void 0,null)),t}return gi(t="r:"+(Kr++).toString(36)),t},unstable_isNewReconciler:!1},Di={readContext:io,useCallback:Li,useContext:io,useEffect:Si,useImperativeHandle:Ci,useLayoutEffect:xi,useMemo:Ai,useReducer:di,useRef:yi,useState:function(){return di(ui)},useDebugValue:Ti,useDeferredValue:function(e){var t=di(ui),n=t[0],r=t[1];return Si((function(){var t=Xo.transition;Xo.transition=1;try{r(e)}finally{Xo.transition=t}}),[e]),n},useTransition:function(){var e=di(ui)[0];return[yi().current,e]},useMutableSource:hi,useOpaqueIdentifier:function(){return di(ui)[0]},unstable_isNewReconciler:!1},Ii={readContext:io,useCallback:Li,useContext:io,useEffect:Si,useImperativeHandle:Ci,useLayoutEffect:xi,useMemo:Ai,useReducer:fi,useRef:yi,useState:function(){return fi(ui)},useDebugValue:Ti,useDeferredValue:function(e){var t=fi(ui),n=t[0],r=t[1];return Si((function(){var t=Xo.transition;Xo.transition=1;try{r(e)}finally{Xo.transition=t}}),[e]),n},useTransition:function(){var e=fi(ui)[0];return[yi().current,e]},useMutableSource:hi,useOpaqueIdentifier:function(){return fi(ui)[0]},unstable_isNewReconciler:!1},Mi=k.ReactCurrentOwner,ji=!1;function Fi(e,t,n,r){t.child=null===e?Lo(t,null,n,r):To(t,e.child,n,r)}function Bi(e,t,n,r,a){n=n.render;var o=t.ref;return oo(t,a),r=li(e,t,n,r,o,a),null===e||ji?(t.flags|=1,Fi(e,t,r,a),t.child):(t.updateQueue=e.updateQueue,t.flags&=-517,e.lanes&=~a,ol(e,t,a))}function zi(e,t,n,r,a,o){if(null===e){var i=n.type;return"function"!=typeof i||Hs(i)||void 0!==i.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=Vs(n.type,null,r,t,t.mode,o)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=i,Ui(e,t,i,r,a,o))}return i=e.child,0==(a&o)&&(a=i.memoizedProps,(n=null!==(n=n.compare)?n:fr)(a,r)&&e.ref===t.ref)?ol(e,t,o):(t.flags|=1,(e=Zs(i,r)).ref=t.ref,e.return=t,t.child=e)}function Ui(e,t,n,r,a,o){if(null!==e&&fr(e.memoizedProps,r)&&e.ref===t.ref){if(ji=!1,0==(o&a))return t.lanes=e.lanes,ol(e,t,o);0!=(16384&e.flags)&&(ji=!0)}return $i(e,t,n,r,o)}function qi(e,t,n){var r=t.pendingProps,a=r.children,o=null!==e?e.memoizedState:null;if("hidden"===r.mode||"unstable-defer-without-hiding"===r.mode)if(0==(4&t.mode))t.memoizedState={baseLanes:0},Es(t,n);else{if(0==(1073741824&n))return e=null!==o?o.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e},Es(t,e),null;t.memoizedState={baseLanes:0},Es(t,null!==o?o.baseLanes:n)}else null!==o?(r=o.baseLanes|n,t.memoizedState=null):r=n,Es(t,r);return Fi(e,t,a,n),t.child}function Gi(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.flags|=128)}function $i(e,t,n,r,a){var o=va(n)?ha:pa.current;return o=ga(t,o),oo(t,a),n=li(e,t,n,r,o,a),null===e||ji?(t.flags|=1,Fi(e,t,n,a),t.child):(t.updateQueue=e.updateQueue,t.flags&=-517,e.lanes&=~a,ol(e,t,a))}function Hi(e,t,n,r,a){if(va(n)){var o=!0;ka(t)}else o=!1;if(oo(t,a),null===t.stateNode)null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),wo(t,n,r),Eo(t,n,r,a),r=!0;else if(null===e){var i=t.stateNode,l=t.memoizedProps;i.props=l;var s=i.context,c=n.contextType;"object"==typeof c&&null!==c?c=io(c):c=ga(t,c=va(n)?ha:pa.current);var u=n.getDerivedStateFromProps,d="function"==typeof u||"function"==typeof i.getSnapshotBeforeUpdate;d||"function"!=typeof i.UNSAFE_componentWillReceiveProps&&"function"!=typeof i.componentWillReceiveProps||(l!==r||s!==c)&&ko(t,i,r,c),lo=!1;var f=t.memoizedState;i.state=f,mo(t,r,i,a),s=t.memoizedState,l!==r||f!==s||ma.current||lo?("function"==typeof u&&(vo(t,n,u,r),s=t.memoizedState),(l=lo||yo(t,n,l,r,f,s,c))?(d||"function"!=typeof i.UNSAFE_componentWillMount&&"function"!=typeof i.componentWillMount||("function"==typeof i.componentWillMount&&i.componentWillMount(),"function"==typeof i.UNSAFE_componentWillMount&&i.UNSAFE_componentWillMount()),"function"==typeof i.componentDidMount&&(t.flags|=4)):("function"==typeof i.componentDidMount&&(t.flags|=4),t.memoizedProps=r,t.memoizedState=s),i.props=r,i.state=s,i.context=c,r=l):("function"==typeof i.componentDidMount&&(t.flags|=4),r=!1)}else{i=t.stateNode,co(e,t),l=t.memoizedProps,c=t.type===t.elementType?l:Qa(t.type,l),i.props=c,d=t.pendingProps,f=i.context,"object"==typeof(s=n.contextType)&&null!==s?s=io(s):s=ga(t,s=va(n)?ha:pa.current);var p=n.getDerivedStateFromProps;(u="function"==typeof p||"function"==typeof i.getSnapshotBeforeUpdate)||"function"!=typeof i.UNSAFE_componentWillReceiveProps&&"function"!=typeof i.componentWillReceiveProps||(l!==d||f!==s)&&ko(t,i,r,s),lo=!1,f=t.memoizedState,i.state=f,mo(t,r,i,a);var m=t.memoizedState;l!==d||f!==m||ma.current||lo?("function"==typeof p&&(vo(t,n,p,r),m=t.memoizedState),(c=lo||yo(t,n,c,r,f,m,s))?(u||"function"!=typeof i.UNSAFE_componentWillUpdate&&"function"!=typeof i.componentWillUpdate||("function"==typeof i.componentWillUpdate&&i.componentWillUpdate(r,m,s),"function"==typeof i.UNSAFE_componentWillUpdate&&i.UNSAFE_componentWillUpdate(r,m,s)),"function"==typeof i.componentDidUpdate&&(t.flags|=4),"function"==typeof i.getSnapshotBeforeUpdate&&(t.flags|=256)):("function"!=typeof i.componentDidUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.flags|=4),"function"!=typeof i.getSnapshotBeforeUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.flags|=256),t.memoizedProps=r,t.memoizedState=m),i.props=r,i.state=m,i.context=s,r=c):("function"!=typeof i.componentDidUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.flags|=4),"function"!=typeof i.getSnapshotBeforeUpdate||l===e.memoizedProps&&f===e.memoizedState||(t.flags|=256),r=!1)}return Zi(e,t,n,r,o,a)}function Zi(e,t,n,r,a,o){Gi(e,t);var i=0!=(64&t.flags);if(!r&&!i)return a&&Ea(t,n,!1),ol(e,t,o);r=t.stateNode,Mi.current=t;var l=i&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.flags|=1,null!==e&&i?(t.child=To(t,e.child,null,o),t.child=To(t,null,l,o)):Fi(e,t,l,o),t.memoizedState=r.state,a&&Ea(t,n,!0),t.child}function Vi(e){var t=e.stateNode;t.pendingContext?ya(0,t.pendingContext,t.pendingContext!==t.context):t.context&&ya(0,t.context,!1),Do(e,t.containerInfo)}var Wi,Yi,Ki,Qi={dehydrated:null,retryLane:0};function Xi(e,t,n){var r,a=t.pendingProps,o=Fo.current,i=!1;return(r=0!=(64&t.flags))||(r=(null===e||null!==e.memoizedState)&&0!=(2&o)),r?(i=!0,t.flags&=-65):null!==e&&null===e.memoizedState||void 0===a.fallback||!0===a.unstable_avoidThisFallback||(o|=1),da(Fo,1&o),null===e?(void 0!==a.fallback&&Ho(t),e=a.children,o=a.fallback,i?(e=Ji(t,e,o,n),t.child.memoizedState={baseLanes:n},t.memoizedState=Qi,e):"number"==typeof a.unstable_expectedLoadTime?(e=Ji(t,e,o,n),t.child.memoizedState={baseLanes:n},t.memoizedState=Qi,t.lanes=33554432,e):((n=Ys({mode:"visible",children:e},t.mode,n,null)).return=t,t.child=n)):(e.memoizedState,i?(a=tl(e,t,a.children,a.fallback,n),i=t.child,o=e.child.memoizedState,i.memoizedState=null===o?{baseLanes:n}:{baseLanes:o.baseLanes|n},i.childLanes=e.childLanes&~n,t.memoizedState=Qi,a):(n=el(e,t,a.children,n),t.memoizedState=null,n))}function Ji(e,t,n,r){var a=e.mode,o=e.child;return t={mode:"hidden",children:t},0==(2&a)&&null!==o?(o.childLanes=0,o.pendingProps=t):o=Ys(t,a,0,null),n=Ws(n,a,r,null),o.return=e,n.return=e,o.sibling=n,e.child=o,n}function el(e,t,n,r){var a=e.child;return e=a.sibling,n=Zs(a,{mode:"visible",children:n}),0==(2&t.mode)&&(n.lanes=r),n.return=t,n.sibling=null,null!==e&&(e.nextEffect=null,e.flags=8,t.firstEffect=t.lastEffect=e),t.child=n}function tl(e,t,n,r,a){var o=t.mode,i=e.child;e=i.sibling;var l={mode:"hidden",children:n};return 0==(2&o)&&t.child!==i?((n=t.child).childLanes=0,n.pendingProps=l,null!==(i=n.lastEffect)?(t.firstEffect=n.firstEffect,t.lastEffect=i,i.nextEffect=null):t.firstEffect=t.lastEffect=null):n=Zs(i,l),null!==e?r=Zs(e,r):(r=Ws(r,o,a,null)).flags|=2,r.return=t,n.return=t,n.sibling=r,t.child=n,r}function nl(e,t){e.lanes|=t;var n=e.alternate;null!==n&&(n.lanes|=t),ao(e.return,t)}function rl(e,t,n,r,a,o){var i=e.memoizedState;null===i?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:a,lastEffect:o}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=r,i.tail=n,i.tailMode=a,i.lastEffect=o)}function al(e,t,n){var r=t.pendingProps,a=r.revealOrder,o=r.tail;if(Fi(e,t,r.children,n),0!=(2&(r=Fo.current)))r=1&r|2,t.flags|=64;else{if(null!==e&&0!=(64&e.flags))e:for(e=t.child;null!==e;){if(13===e.tag)null!==e.memoizedState&&nl(e,n);else if(19===e.tag)nl(e,n);else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(da(Fo,r),0==(2&t.mode))t.memoizedState=null;else switch(a){case"forwards":for(n=t.child,a=null;null!==n;)null!==(e=n.alternate)&&null===Bo(e)&&(a=n),n=n.sibling;null===(n=a)?(a=t.child,t.child=null):(a=n.sibling,n.sibling=null),rl(t,!1,a,n,o,t.lastEffect);break;case"backwards":for(n=null,a=t.child,t.child=null;null!==a;){if(null!==(e=a.alternate)&&null===Bo(e)){t.child=a;break}e=a.sibling,a.sibling=n,n=a,a=e}rl(t,!0,n,null,o,t.lastEffect);break;case"together":rl(t,!1,null,null,void 0,t.lastEffect);break;default:t.memoizedState=null}return t.child}function ol(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),Ul|=t.lanes,0!=(n&t.childLanes)){if(null!==e&&t.child!==e.child)throw Error(i(153));if(null!==t.child){for(n=Zs(e=t.child,e.pendingProps),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Zs(e,e.pendingProps)).return=t;n.sibling=null}return t.child}return null}function il(e,t){if(!qo)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function ll(e,t,n){var r=t.pendingProps;switch(t.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:case 17:return va(t.type)&&ba(),null;case 3:return Io(),ua(ma),ua(pa),Ko(),(r=t.stateNode).pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),null!==e&&null!==e.child||(Vo(t)?t.flags|=4:r.hydrate||(t.flags|=256)),null;case 5:jo(t);var o=Oo(No.current);if(n=t.type,null!==e&&null!=t.stateNode)Yi(e,t,n,r),e.ref!==t.ref&&(t.flags|=128);else{if(!r){if(null===t.stateNode)throw Error(i(166));return null}if(e=Oo(Po.current),Vo(t)){r=t.stateNode,n=t.type;var l=t.memoizedProps;switch(r[Xr]=t,r[Jr]=l,n){case"dialog":Pr("cancel",r),Pr("close",r);break;case"iframe":case"object":case"embed":Pr("load",r);break;case"video":case"audio":for(e=0;e<Cr.length;e++)Pr(Cr[e],r);break;case"source":Pr("error",r);break;case"img":case"image":case"link":Pr("error",r),Pr("load",r);break;case"details":Pr("toggle",r);break;case"input":ee(r,l),Pr("invalid",r);break;case"select":r._wrapperState={wasMultiple:!!l.multiple},Pr("invalid",r);break;case"textarea":se(r,l),Pr("invalid",r)}for(var c in xe(n,l),e=null,l)l.hasOwnProperty(c)&&(o=l[c],"children"===c?"string"==typeof o?r.textContent!==o&&(e=["children",o]):"number"==typeof o&&r.textContent!==""+o&&(e=["children",""+o]):s.hasOwnProperty(c)&&null!=o&&"onScroll"===c&&Pr("scroll",r));switch(n){case"input":K(r),re(r,l,!0);break;case"textarea":K(r),ue(r);break;case"select":case"option":break;default:"function"==typeof l.onClick&&(r.onclick=zr)}r=e,t.updateQueue=r,null!==r&&(t.flags|=4)}else{switch(c=9===o.nodeType?o:o.ownerDocument,e===de&&(e=pe(n)),e===de?"script"===n?((e=c.createElement("div")).innerHTML="<script><\/script>",e=e.removeChild(e.firstChild)):"string"==typeof r.is?e=c.createElement(n,{is:r.is}):(e=c.createElement(n),"select"===n&&(c=e,r.multiple?c.multiple=!0:r.size&&(c.size=r.size))):e=c.createElementNS(e,n),e[Xr]=t,e[Jr]=r,Wi(e,t),t.stateNode=e,c=_e(n,r),n){case"dialog":Pr("cancel",e),Pr("close",e),o=r;break;case"iframe":case"object":case"embed":Pr("load",e),o=r;break;case"video":case"audio":for(o=0;o<Cr.length;o++)Pr(Cr[o],e);o=r;break;case"source":Pr("error",e),o=r;break;case"img":case"image":case"link":Pr("error",e),Pr("load",e),o=r;break;case"details":Pr("toggle",e),o=r;break;case"input":ee(e,r),o=J(e,r),Pr("invalid",e);break;case"option":o=oe(e,r);break;case"select":e._wrapperState={wasMultiple:!!r.multiple},o=a({},r,{value:void 0}),Pr("invalid",e);break;case"textarea":se(e,r),o=le(e,r),Pr("invalid",e);break;default:o=r}xe(n,o);var u=o;for(l in u)if(u.hasOwnProperty(l)){var d=u[l];"style"===l?Ee(e,d):"dangerouslySetInnerHTML"===l?null!=(d=d?d.__html:void 0)&&ve(e,d):"children"===l?"string"==typeof d?("textarea"!==n||""!==d)&&be(e,d):"number"==typeof d&&be(e,""+d):"suppressContentEditableWarning"!==l&&"suppressHydrationWarning"!==l&&"autoFocus"!==l&&(s.hasOwnProperty(l)?null!=d&&"onScroll"===l&&Pr("scroll",e):null!=d&&w(e,l,d,c))}switch(n){case"input":K(e),re(e,r,!1);break;case"textarea":K(e),ue(e);break;case"option":null!=r.value&&e.setAttribute("value",""+W(r.value));break;case"select":e.multiple=!!r.multiple,null!=(l=r.value)?ie(e,!!r.multiple,l,!1):null!=r.defaultValue&&ie(e,!!r.multiple,r.defaultValue,!0);break;default:"function"==typeof o.onClick&&(e.onclick=zr)}Gr(n,r)&&(t.flags|=4)}null!==t.ref&&(t.flags|=128)}return null;case 6:if(e&&null!=t.stateNode)Ki(0,t,e.memoizedProps,r);else{if("string"!=typeof r&&null===t.stateNode)throw Error(i(166));n=Oo(No.current),Oo(Po.current),Vo(t)?(r=t.stateNode,n=t.memoizedProps,r[Xr]=t,r.nodeValue!==n&&(t.flags|=4)):((r=(9===n.nodeType?n:n.ownerDocument).createTextNode(r))[Xr]=t,t.stateNode=r)}return null;case 13:return ua(Fo),r=t.memoizedState,0!=(64&t.flags)?(t.lanes=n,t):(r=null!==r,n=!1,null===e?void 0!==t.memoizedProps.fallback&&Vo(t):n=null!==e.memoizedState,r&&!n&&0!=(2&t.mode)&&(null===e&&!0!==t.memoizedProps.unstable_avoidThisFallback||0!=(1&Fo.current)?0===Fl&&(Fl=3):(0!==Fl&&3!==Fl||(Fl=4),null===Ol||0==(134217727&Ul)&&0==(134217727&ql)||bs(Ol,Il))),(r||n)&&(t.flags|=4),null);case 4:return Io(),null===e&&Nr(t.stateNode.containerInfo),null;case 10:return ro(t),null;case 19:if(ua(Fo),null===(r=t.memoizedState))return null;if(l=0!=(64&t.flags),null===(c=r.rendering))if(l)il(r,!1);else{if(0!==Fl||null!==e&&0!=(64&e.flags))for(e=t.child;null!==e;){if(null!==(c=Bo(e))){for(t.flags|=64,il(r,!1),null!==(l=c.updateQueue)&&(t.updateQueue=l,t.flags|=4),null===r.lastEffect&&(t.firstEffect=null),t.lastEffect=r.lastEffect,r=n,n=t.child;null!==n;)e=r,(l=n).flags&=2,l.nextEffect=null,l.firstEffect=null,l.lastEffect=null,null===(c=l.alternate)?(l.childLanes=0,l.lanes=e,l.child=null,l.memoizedProps=null,l.memoizedState=null,l.updateQueue=null,l.dependencies=null,l.stateNode=null):(l.childLanes=c.childLanes,l.lanes=c.lanes,l.child=c.child,l.memoizedProps=c.memoizedProps,l.memoizedState=c.memoizedState,l.updateQueue=c.updateQueue,l.type=c.type,e=c.dependencies,l.dependencies=null===e?null:{lanes:e.lanes,firstContext:e.firstContext}),n=n.sibling;return da(Fo,1&Fo.current|2),t.child}e=e.sibling}null!==r.tail&&Ga()>Zl&&(t.flags|=64,l=!0,il(r,!1),t.lanes=33554432)}else{if(!l)if(null!==(e=Bo(c))){if(t.flags|=64,l=!0,null!==(n=e.updateQueue)&&(t.updateQueue=n,t.flags|=4),il(r,!0),null===r.tail&&"hidden"===r.tailMode&&!c.alternate&&!qo)return null!==(t=t.lastEffect=r.lastEffect)&&(t.nextEffect=null),null}else 2*Ga()-r.renderingStartTime>Zl&&1073741824!==n&&(t.flags|=64,l=!0,il(r,!1),t.lanes=33554432);r.isBackwards?(c.sibling=t.child,t.child=c):(null!==(n=r.last)?n.sibling=c:t.child=c,r.last=c)}return null!==r.tail?(n=r.tail,r.rendering=n,r.tail=n.sibling,r.lastEffect=t.lastEffect,r.renderingStartTime=Ga(),n.sibling=null,t=Fo.current,da(Fo,l?1&t|2:1&t),n):null;case 23:case 24:return Ss(),null!==e&&null!==e.memoizedState!=(null!==t.memoizedState)&&"unstable-defer-without-hiding"!==r.mode&&(t.flags|=4),null}throw Error(i(156,t.tag))}function sl(e){switch(e.tag){case 1:va(e.type)&&ba();var t=e.flags;return 4096&t?(e.flags=-4097&t|64,e):null;case 3:if(Io(),ua(ma),ua(pa),Ko(),0!=(64&(t=e.flags)))throw Error(i(285));return e.flags=-4097&t|64,e;case 5:return jo(e),null;case 13:return ua(Fo),4096&(t=e.flags)?(e.flags=-4097&t|64,e):null;case 19:return ua(Fo),null;case 4:return Io(),null;case 10:return ro(e),null;case 23:case 24:return Ss(),null;default:return null}}function cl(e,t){try{var n="",r=t;do{n+=Z(r),r=r.return}while(r);var a=n}catch(o){a="\nError generating stack: "+o.message+"\n"+o.stack}return{value:e,source:t,stack:a}}function ul(e,t){try{console.error(t.value)}catch(n){setTimeout((function(){throw n}))}}Wi=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e.appendChild(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},Yi=function(e,t,n,r){var o=e.memoizedProps;if(o!==r){e=t.stateNode,Oo(Po.current);var i,l=null;switch(n){case"input":o=J(e,o),r=J(e,r),l=[];break;case"option":o=oe(e,o),r=oe(e,r),l=[];break;case"select":o=a({},o,{value:void 0}),r=a({},r,{value:void 0}),l=[];break;case"textarea":o=le(e,o),r=le(e,r),l=[];break;default:"function"!=typeof o.onClick&&"function"==typeof r.onClick&&(e.onclick=zr)}for(d in xe(n,r),n=null,o)if(!r.hasOwnProperty(d)&&o.hasOwnProperty(d)&&null!=o[d])if("style"===d){var c=o[d];for(i in c)c.hasOwnProperty(i)&&(n||(n={}),n[i]="")}else"dangerouslySetInnerHTML"!==d&&"children"!==d&&"suppressContentEditableWarning"!==d&&"suppressHydrationWarning"!==d&&"autoFocus"!==d&&(s.hasOwnProperty(d)?l||(l=[]):(l=l||[]).push(d,null));for(d in r){var u=r[d];if(c=null!=o?o[d]:void 0,r.hasOwnProperty(d)&&u!==c&&(null!=u||null!=c))if("style"===d)if(c){for(i in c)!c.hasOwnProperty(i)||u&&u.hasOwnProperty(i)||(n||(n={}),n[i]="");for(i in u)u.hasOwnProperty(i)&&c[i]!==u[i]&&(n||(n={}),n[i]=u[i])}else n||(l||(l=[]),l.push(d,n)),n=u;else"dangerouslySetInnerHTML"===d?(u=u?u.__html:void 0,c=c?c.__html:void 0,null!=u&&c!==u&&(l=l||[]).push(d,u)):"children"===d?"string"!=typeof u&&"number"!=typeof u||(l=l||[]).push(d,""+u):"suppressContentEditableWarning"!==d&&"suppressHydrationWarning"!==d&&(s.hasOwnProperty(d)?(null!=u&&"onScroll"===d&&Pr("scroll",e),l||c===u||(l=[])):"object"==typeof u&&null!==u&&u.$$typeof===I?u.toString():(l=l||[]).push(d,u))}n&&(l=l||[]).push("style",n);var d=l;(t.updateQueue=d)&&(t.flags|=4)}},Ki=function(e,t,n,r){n!==r&&(t.flags|=4)};var dl="function"==typeof WeakMap?WeakMap:Map;function fl(e,t,n){(n=uo(-1,n)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Kl||(Kl=!0,Ql=r),ul(0,t)},n}function pl(e,t,n){(n=uo(-1,n)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var a=t.value;n.payload=function(){return ul(0,t),r(a)}}var o=e.stateNode;return null!==o&&"function"==typeof o.componentDidCatch&&(n.callback=function(){"function"!=typeof r&&(null===Xl?Xl=new Set([this]):Xl.add(this),ul(0,t));var e=t.stack;this.componentDidCatch(t.value,{componentStack:null!==e?e:""})}),n}var ml="function"==typeof WeakSet?WeakSet:Set;function hl(e){var t=e.ref;if(null!==t)if("function"==typeof t)try{t(null)}catch(n){zs(e,n)}else t.current=null}function gl(e,t){switch(t.tag){case 0:case 11:case 15:case 22:case 5:case 6:case 4:case 17:return;case 1:if(256&t.flags&&null!==e){var n=e.memoizedProps,r=e.memoizedState;t=(e=t.stateNode).getSnapshotBeforeUpdate(t.elementType===t.type?n:Qa(t.type,n),r),e.__reactInternalSnapshotBeforeUpdate=t}return;case 3:return void(256&t.flags&&Vr(t.stateNode.containerInfo))}throw Error(i(163))}function vl(e,t,n){switch(n.tag){case 0:case 11:case 15:case 22:if(null!==(t=null!==(t=n.updateQueue)?t.lastEffect:null)){e=t=t.next;do{if(3==(3&e.tag)){var r=e.create;e.destroy=r()}e=e.next}while(e!==t)}if(null!==(t=null!==(t=n.updateQueue)?t.lastEffect:null)){e=t=t.next;do{var a=e;r=a.next,0!=(4&(a=a.tag))&&0!=(1&a)&&(js(n,e),Ms(n,e)),e=r}while(e!==t)}return;case 1:return e=n.stateNode,4&n.flags&&(null===t?e.componentDidMount():(r=n.elementType===n.type?t.memoizedProps:Qa(n.type,t.memoizedProps),e.componentDidUpdate(r,t.memoizedState,e.__reactInternalSnapshotBeforeUpdate))),void(null!==(t=n.updateQueue)&&ho(n,t,e));case 3:if(null!==(t=n.updateQueue)){if(e=null,null!==n.child)switch(n.child.tag){case 5:case 1:e=n.child.stateNode}ho(n,t,e)}return;case 5:return e=n.stateNode,void(null===t&&4&n.flags&&Gr(n.type,n.memoizedProps)&&e.focus());case 6:case 4:case 12:case 19:case 17:case 20:case 21:case 23:case 24:return;case 13:return void(null===n.memoizedState&&(n=n.alternate,null!==n&&(n=n.memoizedState,null!==n&&(n=n.dehydrated,null!==n&&Et(n)))))}throw Error(i(163))}function bl(e,t){for(var n=e;;){if(5===n.tag){var r=n.stateNode;if(t)"function"==typeof(r=r.style).setProperty?r.setProperty("display","none","important"):r.display="none";else{r=n.stateNode;var a=n.memoizedProps.style;a=null!=a&&a.hasOwnProperty("display")?a.display:null,r.style.display=ke("display",a)}}else if(6===n.tag)n.stateNode.nodeValue=t?"":n.memoizedProps;else if((23!==n.tag&&24!==n.tag||null===n.memoizedState||n===e)&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===e)break;for(;null===n.sibling;){if(null===n.return||n.return===e)return;n=n.return}n.sibling.return=n.return,n=n.sibling}}function yl(e,t){if(xa&&"function"==typeof xa.onCommitFiberUnmount)try{xa.onCommitFiberUnmount(Sa,t)}catch(o){}switch(t.tag){case 0:case 11:case 14:case 15:case 22:if(null!==(e=t.updateQueue)&&null!==(e=e.lastEffect)){var n=e=e.next;do{var r=n,a=r.destroy;if(r=r.tag,void 0!==a)if(0!=(4&r))js(t,n);else{r=t;try{a()}catch(o){zs(r,o)}}n=n.next}while(n!==e)}break;case 1:if(hl(t),"function"==typeof(e=t.stateNode).componentWillUnmount)try{e.props=t.memoizedProps,e.state=t.memoizedState,e.componentWillUnmount()}catch(o){zs(t,o)}break;case 5:hl(t);break;case 4:_l(e,t)}}function wl(e){e.alternate=null,e.child=null,e.dependencies=null,e.firstEffect=null,e.lastEffect=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.return=null,e.updateQueue=null}function kl(e){return 5===e.tag||3===e.tag||4===e.tag}function El(e){e:{for(var t=e.return;null!==t;){if(kl(t))break e;t=t.return}throw Error(i(160))}var n=t;switch(t=n.stateNode,n.tag){case 5:var r=!1;break;case 3:case 4:t=t.containerInfo,r=!0;break;default:throw Error(i(161))}16&n.flags&&(be(t,""),n.flags&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||kl(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag&&18!==n.tag;){if(2&n.flags)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(2&n.flags)){n=n.stateNode;break e}}r?Sl(e,n,t):xl(e,n,t)}function Sl(e,t,n){var r=e.tag,a=5===r||6===r;if(a)e=a?e.stateNode:e.stateNode.instance,t?8===n.nodeType?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(8===n.nodeType?(t=n.parentNode).insertBefore(e,n):(t=n).appendChild(e),null!=(n=n._reactRootContainer)||null!==t.onclick||(t.onclick=zr));else if(4!==r&&null!==(e=e.child))for(Sl(e,t,n),e=e.sibling;null!==e;)Sl(e,t,n),e=e.sibling}function xl(e,t,n){var r=e.tag,a=5===r||6===r;if(a)e=a?e.stateNode:e.stateNode.instance,t?n.insertBefore(e,t):n.appendChild(e);else if(4!==r&&null!==(e=e.child))for(xl(e,t,n),e=e.sibling;null!==e;)xl(e,t,n),e=e.sibling}function _l(e,t){for(var n,r,a=t,o=!1;;){if(!o){o=a.return;e:for(;;){if(null===o)throw Error(i(160));switch(n=o.stateNode,o.tag){case 5:r=!1;break e;case 3:case 4:n=n.containerInfo,r=!0;break e}o=o.return}o=!0}if(5===a.tag||6===a.tag){e:for(var l=e,s=a,c=s;;)if(yl(l,c),null!==c.child&&4!==c.tag)c.child.return=c,c=c.child;else{if(c===s)break e;for(;null===c.sibling;){if(null===c.return||c.return===s)break e;c=c.return}c.sibling.return=c.return,c=c.sibling}r?(l=n,s=a.stateNode,8===l.nodeType?l.parentNode.removeChild(s):l.removeChild(s)):n.removeChild(a.stateNode)}else if(4===a.tag){if(null!==a.child){n=a.stateNode.containerInfo,r=!0,a.child.return=a,a=a.child;continue}}else if(yl(e,a),null!==a.child){a.child.return=a,a=a.child;continue}if(a===t)break;for(;null===a.sibling;){if(null===a.return||a.return===t)return;4===(a=a.return).tag&&(o=!1)}a.sibling.return=a.return,a=a.sibling}}function Cl(e,t){switch(t.tag){case 0:case 11:case 14:case 15:case 22:var n=t.updateQueue;if(null!==(n=null!==n?n.lastEffect:null)){var r=n=n.next;do{3==(3&r.tag)&&(e=r.destroy,r.destroy=void 0,void 0!==e&&e()),r=r.next}while(r!==n)}return;case 1:case 12:case 17:return;case 5:if(null!=(n=t.stateNode)){r=t.memoizedProps;var a=null!==e?e.memoizedProps:r;e=t.type;var o=t.updateQueue;if(t.updateQueue=null,null!==o){for(n[Jr]=r,"input"===e&&"radio"===r.type&&null!=r.name&&te(n,r),_e(e,a),t=_e(e,r),a=0;a<o.length;a+=2){var l=o[a],s=o[a+1];"style"===l?Ee(n,s):"dangerouslySetInnerHTML"===l?ve(n,s):"children"===l?be(n,s):w(n,l,s,t)}switch(e){case"input":ne(n,r);break;case"textarea":ce(n,r);break;case"select":e=n._wrapperState.wasMultiple,n._wrapperState.wasMultiple=!!r.multiple,null!=(o=r.value)?ie(n,!!r.multiple,o,!1):e!==!!r.multiple&&(null!=r.defaultValue?ie(n,!!r.multiple,r.defaultValue,!0):ie(n,!!r.multiple,r.multiple?[]:"",!1))}}}return;case 6:if(null===t.stateNode)throw Error(i(162));return void(t.stateNode.nodeValue=t.memoizedProps);case 3:return void((n=t.stateNode).hydrate&&(n.hydrate=!1,Et(n.containerInfo)));case 13:return null!==t.memoizedState&&(Hl=Ga(),bl(t.child,!0)),void Tl(t);case 19:return void Tl(t);case 23:case 24:return void bl(t,null!==t.memoizedState)}throw Error(i(163))}function Tl(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new ml),t.forEach((function(t){var r=qs.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))}))}}function Ll(e,t){return null!==e&&(null===(e=e.memoizedState)||null!==e.dehydrated)&&(null!==(t=t.memoizedState)&&null===t.dehydrated)}var Al=Math.ceil,Pl=k.ReactCurrentDispatcher,Rl=k.ReactCurrentOwner,Nl=0,Ol=null,Dl=null,Il=0,Ml=0,jl=ca(0),Fl=0,Bl=null,zl=0,Ul=0,ql=0,Gl=0,$l=null,Hl=0,Zl=1/0;function Vl(){Zl=Ga()+500}var Wl,Yl=null,Kl=!1,Ql=null,Xl=null,Jl=!1,es=null,ts=90,ns=[],rs=[],as=null,os=0,is=null,ls=-1,ss=0,cs=0,us=null,ds=!1;function fs(){return 0!=(48&Nl)?Ga():-1!==ls?ls:ls=Ga()}function ps(e){if(0==(2&(e=e.mode)))return 1;if(0==(4&e))return 99===$a()?1:2;if(0===ss&&(ss=zl),0!==Ka.transition){0!==cs&&(cs=null!==$l?$l.pendingLanes:0),e=ss;var t=4186112&~cs;return 0===(t&=-t)&&(0===(t=(e=4186112&~e)&-e)&&(t=8192)),t}return e=$a(),0!=(4&Nl)&&98===e?e=zt(12,ss):e=zt(e=function(e){switch(e){case 99:return 15;case 98:return 10;case 97:case 96:return 8;case 95:return 2;default:return 0}}(e),ss),e}function ms(e,t,n){if(50<os)throw os=0,is=null,Error(i(185));if(null===(e=hs(e,t)))return null;Gt(e,t,n),e===Ol&&(ql|=t,4===Fl&&bs(e,Il));var r=$a();1===t?0!=(8&Nl)&&0==(48&Nl)?ys(e):(gs(e,n),0===Nl&&(Vl(),Wa())):(0==(4&Nl)||98!==r&&99!==r||(null===as?as=new Set([e]):as.add(e)),gs(e,n)),$l=e}function hs(e,t){e.lanes|=t;var n=e.alternate;for(null!==n&&(n.lanes|=t),n=e,e=e.return;null!==e;)e.childLanes|=t,null!==(n=e.alternate)&&(n.childLanes|=t),n=e,e=e.return;return 3===n.tag?n.stateNode:null}function gs(e,t){for(var n=e.callbackNode,r=e.suspendedLanes,a=e.pingedLanes,o=e.expirationTimes,l=e.pendingLanes;0<l;){var s=31-$t(l),c=1<<s,u=o[s];if(-1===u){if(0==(c&r)||0!=(c&a)){u=t,jt(c);var d=Mt;o[s]=10<=d?u+250:6<=d?u+5e3:-1}}else u<=t&&(e.expiredLanes|=c);l&=~c}if(r=Ft(e,e===Ol?Il:0),t=Mt,0===r)null!==n&&(n!==ja&&Ta(n),e.callbackNode=null,e.callbackPriority=0);else{if(null!==n){if(e.callbackPriority===t)return;n!==ja&&Ta(n)}15===t?(n=ys.bind(null,e),null===Ba?(Ba=[n],za=Ca(Na,Ya)):Ba.push(n),n=ja):14===t?n=Va(99,ys.bind(null,e)):(n=function(e){switch(e){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(i(358,e))}}(t),n=Va(n,vs.bind(null,e))),e.callbackPriority=t,e.callbackNode=n}}function vs(e){if(ls=-1,cs=ss=0,0!=(48&Nl))throw Error(i(327));var t=e.callbackNode;if(Is()&&e.callbackNode!==t)return null;var n=Ft(e,e===Ol?Il:0);if(0===n)return null;var r=n,a=Nl;Nl|=16;var o=Cs();for(Ol===e&&Il===r||(Vl(),xs(e,r));;)try{As();break}catch(s){_s(e,s)}if(no(),Pl.current=o,Nl=a,null!==Dl?r=0:(Ol=null,Il=0,r=Fl),0!=(zl&ql))xs(e,0);else if(0!==r){if(2===r&&(Nl|=64,e.hydrate&&(e.hydrate=!1,Vr(e.containerInfo)),0!==(n=Bt(e))&&(r=Ts(e,n))),1===r)throw t=Bl,xs(e,0),bs(e,n),gs(e,Ga()),t;switch(e.finishedWork=e.current.alternate,e.finishedLanes=n,r){case 0:case 1:throw Error(i(345));case 2:case 5:Ns(e);break;case 3:if(bs(e,n),(62914560&n)===n&&10<(r=Hl+500-Ga())){if(0!==Ft(e,0))break;if(((a=e.suspendedLanes)&n)!==n){fs(),e.pingedLanes|=e.suspendedLanes&a;break}e.timeoutHandle=Hr(Ns.bind(null,e),r);break}Ns(e);break;case 4:if(bs(e,n),(4186112&n)===n)break;for(r=e.eventTimes,a=-1;0<n;){var l=31-$t(n);o=1<<l,(l=r[l])>a&&(a=l),n&=~o}if(n=a,10<(n=(120>(n=Ga()-n)?120:480>n?480:1080>n?1080:1920>n?1920:3e3>n?3e3:4320>n?4320:1960*Al(n/1960))-n)){e.timeoutHandle=Hr(Ns.bind(null,e),n);break}Ns(e);break;default:throw Error(i(329))}}return gs(e,Ga()),e.callbackNode===t?vs.bind(null,e):null}function bs(e,t){for(t&=~Gl,t&=~ql,e.suspendedLanes|=t,e.pingedLanes&=~t,e=e.expirationTimes;0<t;){var n=31-$t(t),r=1<<n;e[n]=-1,t&=~r}}function ys(e){if(0!=(48&Nl))throw Error(i(327));if(Is(),e===Ol&&0!=(e.expiredLanes&Il)){var t=Il,n=Ts(e,t);0!=(zl&ql)&&(n=Ts(e,t=Ft(e,t)))}else n=Ts(e,t=Ft(e,0));if(0!==e.tag&&2===n&&(Nl|=64,e.hydrate&&(e.hydrate=!1,Vr(e.containerInfo)),0!==(t=Bt(e))&&(n=Ts(e,t))),1===n)throw n=Bl,xs(e,0),bs(e,t),gs(e,Ga()),n;return e.finishedWork=e.current.alternate,e.finishedLanes=t,Ns(e),gs(e,Ga()),null}function ws(e,t){var n=Nl;Nl|=1;try{return e(t)}finally{0===(Nl=n)&&(Vl(),Wa())}}function ks(e,t){var n=Nl;Nl&=-2,Nl|=8;try{return e(t)}finally{0===(Nl=n)&&(Vl(),Wa())}}function Es(e,t){da(jl,Ml),Ml|=t,zl|=t}function Ss(){Ml=jl.current,ua(jl)}function xs(e,t){e.finishedWork=null,e.finishedLanes=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,Zr(n)),null!==Dl)for(n=Dl.return;null!==n;){var r=n;switch(r.tag){case 1:null!=(r=r.type.childContextTypes)&&ba();break;case 3:Io(),ua(ma),ua(pa),Ko();break;case 5:jo(r);break;case 4:Io();break;case 13:case 19:ua(Fo);break;case 10:ro(r);break;case 23:case 24:Ss()}n=n.return}Ol=e,Dl=Zs(e.current,null),Il=Ml=zl=t,Fl=0,Bl=null,Gl=ql=Ul=0}function _s(e,t){for(;;){var n=Dl;try{if(no(),Qo.current=Ni,ri){for(var r=ei.memoizedState;null!==r;){var a=r.queue;null!==a&&(a.pending=null),r=r.next}ri=!1}if(Jo=0,ni=ti=ei=null,ai=!1,Rl.current=null,null===n||null===n.return){Fl=1,Bl=t,Dl=null;break}e:{var o=e,i=n.return,l=n,s=t;if(t=Il,l.flags|=2048,l.firstEffect=l.lastEffect=null,null!==s&&"object"==typeof s&&"function"==typeof s.then){var c=s;if(0==(2&l.mode)){var u=l.alternate;u?(l.updateQueue=u.updateQueue,l.memoizedState=u.memoizedState,l.lanes=u.lanes):(l.updateQueue=null,l.memoizedState=null)}var d=0!=(1&Fo.current),f=i;do{var p;if(p=13===f.tag){var m=f.memoizedState;if(null!==m)p=null!==m.dehydrated;else{var h=f.memoizedProps;p=void 0!==h.fallback&&(!0!==h.unstable_avoidThisFallback||!d)}}if(p){var g=f.updateQueue;if(null===g){var v=new Set;v.add(c),f.updateQueue=v}else g.add(c);if(0==(2&f.mode)){if(f.flags|=64,l.flags|=16384,l.flags&=-2981,1===l.tag)if(null===l.alternate)l.tag=17;else{var b=uo(-1,1);b.tag=2,fo(l,b)}l.lanes|=1;break e}s=void 0,l=t;var y=o.pingCache;if(null===y?(y=o.pingCache=new dl,s=new Set,y.set(c,s)):void 0===(s=y.get(c))&&(s=new Set,y.set(c,s)),!s.has(l)){s.add(l);var w=Us.bind(null,o,c,l);c.then(w,w)}f.flags|=4096,f.lanes=t;break e}f=f.return}while(null!==f);s=Error((V(l.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.")}5!==Fl&&(Fl=2),s=cl(s,l),f=i;do{switch(f.tag){case 3:o=s,f.flags|=4096,t&=-t,f.lanes|=t,po(f,fl(0,o,t));break e;case 1:o=s;var k=f.type,E=f.stateNode;if(0==(64&f.flags)&&("function"==typeof k.getDerivedStateFromError||null!==E&&"function"==typeof E.componentDidCatch&&(null===Xl||!Xl.has(E)))){f.flags|=4096,t&=-t,f.lanes|=t,po(f,pl(f,o,t));break e}}f=f.return}while(null!==f)}Rs(n)}catch(S){t=S,Dl===n&&null!==n&&(Dl=n=n.return);continue}break}}function Cs(){var e=Pl.current;return Pl.current=Ni,null===e?Ni:e}function Ts(e,t){var n=Nl;Nl|=16;var r=Cs();for(Ol===e&&Il===t||xs(e,t);;)try{Ls();break}catch(a){_s(e,a)}if(no(),Nl=n,Pl.current=r,null!==Dl)throw Error(i(261));return Ol=null,Il=0,Fl}function Ls(){for(;null!==Dl;)Ps(Dl)}function As(){for(;null!==Dl&&!La();)Ps(Dl)}function Ps(e){var t=Wl(e.alternate,e,Ml);e.memoizedProps=e.pendingProps,null===t?Rs(e):Dl=t,Rl.current=null}function Rs(e){var t=e;do{var n=t.alternate;if(e=t.return,0==(2048&t.flags)){if(null!==(n=ll(n,t,Ml)))return void(Dl=n);if(24!==(n=t).tag&&23!==n.tag||null===n.memoizedState||0!=(1073741824&Ml)||0==(4&n.mode)){for(var r=0,a=n.child;null!==a;)r|=a.lanes|a.childLanes,a=a.sibling;n.childLanes=r}null!==e&&0==(2048&e.flags)&&(null===e.firstEffect&&(e.firstEffect=t.firstEffect),null!==t.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=t.firstEffect),e.lastEffect=t.lastEffect),1<t.flags&&(null!==e.lastEffect?e.lastEffect.nextEffect=t:e.firstEffect=t,e.lastEffect=t))}else{if(null!==(n=sl(t)))return n.flags&=2047,void(Dl=n);null!==e&&(e.firstEffect=e.lastEffect=null,e.flags|=2048)}if(null!==(t=t.sibling))return void(Dl=t);Dl=t=e}while(null!==t);0===Fl&&(Fl=5)}function Ns(e){var t=$a();return Za(99,Os.bind(null,e,t)),null}function Os(e,t){do{Is()}while(null!==es);if(0!=(48&Nl))throw Error(i(327));var n=e.finishedWork;if(null===n)return null;if(e.finishedWork=null,e.finishedLanes=0,n===e.current)throw Error(i(177));e.callbackNode=null;var r=n.lanes|n.childLanes,a=r,o=e.pendingLanes&~a;e.pendingLanes=a,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=a,e.mutableReadLanes&=a,e.entangledLanes&=a,a=e.entanglements;for(var l=e.eventTimes,s=e.expirationTimes;0<o;){var c=31-$t(o),u=1<<c;a[c]=0,l[c]=-1,s[c]=-1,o&=~u}if(null!==as&&0==(24&r)&&as.has(e)&&as.delete(e),e===Ol&&(Dl=Ol=null,Il=0),1<n.flags?null!==n.lastEffect?(n.lastEffect.nextEffect=n,r=n.firstEffect):r=n:r=n.firstEffect,null!==r){if(a=Nl,Nl|=32,Rl.current=null,Ur=Yt,vr(l=gr())){if("selectionStart"in l)s={start:l.selectionStart,end:l.selectionEnd};else e:if(s=(s=l.ownerDocument)&&s.defaultView||window,(u=s.getSelection&&s.getSelection())&&0!==u.rangeCount){s=u.anchorNode,o=u.anchorOffset,c=u.focusNode,u=u.focusOffset;try{s.nodeType,c.nodeType}catch(C){s=null;break e}var d=0,f=-1,p=-1,m=0,h=0,g=l,v=null;t:for(;;){for(var b;g!==s||0!==o&&3!==g.nodeType||(f=d+o),g!==c||0!==u&&3!==g.nodeType||(p=d+u),3===g.nodeType&&(d+=g.nodeValue.length),null!==(b=g.firstChild);)v=g,g=b;for(;;){if(g===l)break t;if(v===s&&++m===o&&(f=d),v===c&&++h===u&&(p=d),null!==(b=g.nextSibling))break;v=(g=v).parentNode}g=b}s=-1===f||-1===p?null:{start:f,end:p}}else s=null;s=s||{start:0,end:0}}else s=null;qr={focusedElem:l,selectionRange:s},Yt=!1,us=null,ds=!1,Yl=r;do{try{Ds()}catch(C){if(null===Yl)throw Error(i(330));zs(Yl,C),Yl=Yl.nextEffect}}while(null!==Yl);us=null,Yl=r;do{try{for(l=e;null!==Yl;){var y=Yl.flags;if(16&y&&be(Yl.stateNode,""),128&y){var w=Yl.alternate;if(null!==w){var k=w.ref;null!==k&&("function"==typeof k?k(null):k.current=null)}}switch(1038&y){case 2:El(Yl),Yl.flags&=-3;break;case 6:El(Yl),Yl.flags&=-3,Cl(Yl.alternate,Yl);break;case 1024:Yl.flags&=-1025;break;case 1028:Yl.flags&=-1025,Cl(Yl.alternate,Yl);break;case 4:Cl(Yl.alternate,Yl);break;case 8:_l(l,s=Yl);var E=s.alternate;wl(s),null!==E&&wl(E)}Yl=Yl.nextEffect}}catch(C){if(null===Yl)throw Error(i(330));zs(Yl,C),Yl=Yl.nextEffect}}while(null!==Yl);if(k=qr,w=gr(),y=k.focusedElem,l=k.selectionRange,w!==y&&y&&y.ownerDocument&&hr(y.ownerDocument.documentElement,y)){null!==l&&vr(y)&&(w=l.start,void 0===(k=l.end)&&(k=w),"selectionStart"in y?(y.selectionStart=w,y.selectionEnd=Math.min(k,y.value.length)):(k=(w=y.ownerDocument||document)&&w.defaultView||window).getSelection&&(k=k.getSelection(),s=y.textContent.length,E=Math.min(l.start,s),l=void 0===l.end?E:Math.min(l.end,s),!k.extend&&E>l&&(s=l,l=E,E=s),s=mr(y,E),o=mr(y,l),s&&o&&(1!==k.rangeCount||k.anchorNode!==s.node||k.anchorOffset!==s.offset||k.focusNode!==o.node||k.focusOffset!==o.offset)&&((w=w.createRange()).setStart(s.node,s.offset),k.removeAllRanges(),E>l?(k.addRange(w),k.extend(o.node,o.offset)):(w.setEnd(o.node,o.offset),k.addRange(w))))),w=[];for(k=y;k=k.parentNode;)1===k.nodeType&&w.push({element:k,left:k.scrollLeft,top:k.scrollTop});for("function"==typeof y.focus&&y.focus(),y=0;y<w.length;y++)(k=w[y]).element.scrollLeft=k.left,k.element.scrollTop=k.top}Yt=!!Ur,qr=Ur=null,e.current=n,Yl=r;do{try{for(y=e;null!==Yl;){var S=Yl.flags;if(36&S&&vl(y,Yl.alternate,Yl),128&S){w=void 0;var x=Yl.ref;if(null!==x){var _=Yl.stateNode;Yl.tag,w=_,"function"==typeof x?x(w):x.current=w}}Yl=Yl.nextEffect}}catch(C){if(null===Yl)throw Error(i(330));zs(Yl,C),Yl=Yl.nextEffect}}while(null!==Yl);Yl=null,Fa(),Nl=a}else e.current=n;if(Jl)Jl=!1,es=e,ts=t;else for(Yl=r;null!==Yl;)t=Yl.nextEffect,Yl.nextEffect=null,8&Yl.flags&&((S=Yl).sibling=null,S.stateNode=null),Yl=t;if(0===(r=e.pendingLanes)&&(Xl=null),1===r?e===is?os++:(os=0,is=e):os=0,n=n.stateNode,xa&&"function"==typeof xa.onCommitFiberRoot)try{xa.onCommitFiberRoot(Sa,n,void 0,64==(64&n.current.flags))}catch(C){}if(gs(e,Ga()),Kl)throw Kl=!1,e=Ql,Ql=null,e;return 0!=(8&Nl)||Wa(),null}function Ds(){for(;null!==Yl;){var e=Yl.alternate;ds||null===us||(0!=(8&Yl.flags)?et(Yl,us)&&(ds=!0):13===Yl.tag&&Ll(e,Yl)&&et(Yl,us)&&(ds=!0));var t=Yl.flags;0!=(256&t)&&gl(e,Yl),0==(512&t)||Jl||(Jl=!0,Va(97,(function(){return Is(),null}))),Yl=Yl.nextEffect}}function Is(){if(90!==ts){var e=97<ts?97:ts;return ts=90,Za(e,Fs)}return!1}function Ms(e,t){ns.push(t,e),Jl||(Jl=!0,Va(97,(function(){return Is(),null})))}function js(e,t){rs.push(t,e),Jl||(Jl=!0,Va(97,(function(){return Is(),null})))}function Fs(){if(null===es)return!1;var e=es;if(es=null,0!=(48&Nl))throw Error(i(331));var t=Nl;Nl|=32;var n=rs;rs=[];for(var r=0;r<n.length;r+=2){var a=n[r],o=n[r+1],l=a.destroy;if(a.destroy=void 0,"function"==typeof l)try{l()}catch(c){if(null===o)throw Error(i(330));zs(o,c)}}for(n=ns,ns=[],r=0;r<n.length;r+=2){a=n[r],o=n[r+1];try{var s=a.create;a.destroy=s()}catch(c){if(null===o)throw Error(i(330));zs(o,c)}}for(s=e.current.firstEffect;null!==s;)e=s.nextEffect,s.nextEffect=null,8&s.flags&&(s.sibling=null,s.stateNode=null),s=e;return Nl=t,Wa(),!0}function Bs(e,t,n){fo(e,t=fl(0,t=cl(n,t),1)),t=fs(),null!==(e=hs(e,1))&&(Gt(e,1,t),gs(e,t))}function zs(e,t){if(3===e.tag)Bs(e,e,t);else for(var n=e.return;null!==n;){if(3===n.tag){Bs(n,e,t);break}if(1===n.tag){var r=n.stateNode;if("function"==typeof n.type.getDerivedStateFromError||"function"==typeof r.componentDidCatch&&(null===Xl||!Xl.has(r))){var a=pl(n,e=cl(t,e),1);if(fo(n,a),a=fs(),null!==(n=hs(n,1)))Gt(n,1,a),gs(n,a);else if("function"==typeof r.componentDidCatch&&(null===Xl||!Xl.has(r)))try{r.componentDidCatch(t,e)}catch(o){}break}}n=n.return}}function Us(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),t=fs(),e.pingedLanes|=e.suspendedLanes&n,Ol===e&&(Il&n)===n&&(4===Fl||3===Fl&&(62914560&Il)===Il&&500>Ga()-Hl?xs(e,0):Gl|=n),gs(e,t)}function qs(e,t){var n=e.stateNode;null!==n&&n.delete(t),0===(t=0)&&(0==(2&(t=e.mode))?t=1:0==(4&t)?t=99===$a()?1:2:(0===ss&&(ss=zl),0===(t=Ut(62914560&~ss))&&(t=4194304))),n=fs(),null!==(e=hs(e,t))&&(Gt(e,t,n),gs(e,n))}function Gs(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.flags=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.childLanes=this.lanes=0,this.alternate=null}function $s(e,t,n,r){return new Gs(e,t,n,r)}function Hs(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Zs(e,t){var n=e.alternate;return null===n?((n=$s(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.nextEffect=null,n.firstEffect=null,n.lastEffect=null),n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Vs(e,t,n,r,a,o){var l=2;if(r=e,"function"==typeof e)Hs(e)&&(l=1);else if("string"==typeof e)l=5;else e:switch(e){case x:return Ws(n.children,a,o,t);case M:l=8,a|=16;break;case _:l=8,a|=1;break;case C:return(e=$s(12,n,t,8|a)).elementType=C,e.type=C,e.lanes=o,e;case P:return(e=$s(13,n,t,a)).type=P,e.elementType=P,e.lanes=o,e;case R:return(e=$s(19,n,t,a)).elementType=R,e.lanes=o,e;case j:return Ys(n,a,o,t);case F:return(e=$s(24,n,t,a)).elementType=F,e.lanes=o,e;default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case T:l=10;break e;case L:l=9;break e;case A:l=11;break e;case N:l=14;break e;case O:l=16,r=null;break e;case D:l=22;break e}throw Error(i(130,null==e?e:typeof e,""))}return(t=$s(l,n,t,a)).elementType=e,t.type=r,t.lanes=o,t}function Ws(e,t,n,r){return(e=$s(7,e,r,t)).lanes=n,e}function Ys(e,t,n,r){return(e=$s(23,e,r,t)).elementType=j,e.lanes=n,e}function Ks(e,t,n){return(e=$s(6,e,null,t)).lanes=n,e}function Qs(e,t,n){return(t=$s(4,null!==e.children?e.children:[],e.key,t)).lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Xs(e,t,n){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.pendingContext=this.context=null,this.hydrate=n,this.callbackNode=null,this.callbackPriority=0,this.eventTimes=qt(0),this.expirationTimes=qt(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=qt(0),this.mutableSourceEagerHydrationData=null}function Js(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:S,key:null==r?null:""+r,children:e,containerInfo:t,implementation:n}}function ec(e,t,n,r){var a=t.current,o=fs(),l=ps(a);e:if(n){t:{if(Ke(n=n._reactInternals)!==n||1!==n.tag)throw Error(i(170));var s=n;do{switch(s.tag){case 3:s=s.stateNode.context;break t;case 1:if(va(s.type)){s=s.stateNode.__reactInternalMemoizedMergedChildContext;break t}}s=s.return}while(null!==s);throw Error(i(171))}if(1===n.tag){var c=n.type;if(va(c)){n=wa(n,c,s);break e}}n=s}else n=fa;return null===t.context?t.context=n:t.pendingContext=n,(t=uo(o,l)).payload={element:e},null!==(r=void 0===r?null:r)&&(t.callback=r),fo(a,t),ms(a,l,o),l}function tc(e){return(e=e.current).child?(e.child.tag,e.child.stateNode):null}function nc(e,t){if(null!==(e=e.memoizedState)&&null!==e.dehydrated){var n=e.retryLane;e.retryLane=0!==n&&n<t?n:t}}function rc(e,t){nc(e,t),(e=e.alternate)&&nc(e,t)}function ac(e,t,n){var r=null!=n&&null!=n.hydrationOptions&&n.hydrationOptions.mutableSources||null;if(n=new Xs(e,t,null!=n&&!0===n.hydrate),t=$s(3,null,null,2===t?7:1===t?3:0),n.current=t,t.stateNode=n,so(t),e[ea]=n.current,Nr(8===e.nodeType?e.parentNode:e),r)for(e=0;e<r.length;e++){var a=(t=r[e])._getVersion;a=a(t._source),null==n.mutableSourceEagerHydrationData?n.mutableSourceEagerHydrationData=[t,a]:n.mutableSourceEagerHydrationData.push(t,a)}this._internalRoot=n}function oc(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType&&(8!==e.nodeType||" react-mount-point-unstable "!==e.nodeValue))}function ic(e,t,n,r,a){var o=n._reactRootContainer;if(o){var i=o._internalRoot;if("function"==typeof a){var l=a;a=function(){var e=tc(i);l.call(e)}}ec(t,i,e,a)}else{if(o=n._reactRootContainer=function(e,t){if(t||(t=!(!(t=e?9===e.nodeType?e.documentElement:e.firstChild:null)||1!==t.nodeType||!t.hasAttribute("data-reactroot"))),!t)for(var n;n=e.lastChild;)e.removeChild(n);return new ac(e,0,t?{hydrate:!0}:void 0)}(n,r),i=o._internalRoot,"function"==typeof a){var s=a;a=function(){var e=tc(i);s.call(e)}}ks((function(){ec(t,i,e,a)}))}return tc(i)}function lc(e,t){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!oc(t))throw Error(i(200));return Js(e,t,null,n)}Wl=function(e,t,n){var r=t.lanes;if(null!==e)if(e.memoizedProps!==t.pendingProps||ma.current)ji=!0;else{if(0==(n&r)){switch(ji=!1,t.tag){case 3:Vi(t),Wo();break;case 5:Mo(t);break;case 1:va(t.type)&&ka(t);break;case 4:Do(t,t.stateNode.containerInfo);break;case 10:r=t.memoizedProps.value;var a=t.type._context;da(Xa,a._currentValue),a._currentValue=r;break;case 13:if(null!==t.memoizedState)return 0!=(n&t.child.childLanes)?Xi(e,t,n):(da(Fo,1&Fo.current),null!==(t=ol(e,t,n))?t.sibling:null);da(Fo,1&Fo.current);break;case 19:if(r=0!=(n&t.childLanes),0!=(64&e.flags)){if(r)return al(e,t,n);t.flags|=64}if(null!==(a=t.memoizedState)&&(a.rendering=null,a.tail=null,a.lastEffect=null),da(Fo,Fo.current),r)break;return null;case 23:case 24:return t.lanes=0,qi(e,t,n)}return ol(e,t,n)}ji=0!=(16384&e.flags)}else ji=!1;switch(t.lanes=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),e=t.pendingProps,a=ga(t,pa.current),oo(t,n),a=li(null,t,r,e,a,n),t.flags|=1,"object"==typeof a&&null!==a&&"function"==typeof a.render&&void 0===a.$$typeof){if(t.tag=1,t.memoizedState=null,t.updateQueue=null,va(r)){var o=!0;ka(t)}else o=!1;t.memoizedState=null!==a.state&&void 0!==a.state?a.state:null,so(t);var l=r.getDerivedStateFromProps;"function"==typeof l&&vo(t,r,l,e),a.updater=bo,t.stateNode=a,a._reactInternals=t,Eo(t,r,e,n),t=Zi(null,t,r,!0,o,n)}else t.tag=0,Fi(null,t,a,n),t=t.child;return t;case 16:a=t.elementType;e:{switch(null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),e=t.pendingProps,a=(o=a._init)(a._payload),t.type=a,o=t.tag=function(e){if("function"==typeof e)return Hs(e)?1:0;if(null!=e){if((e=e.$$typeof)===A)return 11;if(e===N)return 14}return 2}(a),e=Qa(a,e),o){case 0:t=$i(null,t,a,e,n);break e;case 1:t=Hi(null,t,a,e,n);break e;case 11:t=Bi(null,t,a,e,n);break e;case 14:t=zi(null,t,a,Qa(a.type,e),r,n);break e}throw Error(i(306,a,""))}return t;case 0:return r=t.type,a=t.pendingProps,$i(e,t,r,a=t.elementType===r?a:Qa(r,a),n);case 1:return r=t.type,a=t.pendingProps,Hi(e,t,r,a=t.elementType===r?a:Qa(r,a),n);case 3:if(Vi(t),r=t.updateQueue,null===e||null===r)throw Error(i(282));if(r=t.pendingProps,a=null!==(a=t.memoizedState)?a.element:null,co(e,t),mo(t,r,null,n),(r=t.memoizedState.element)===a)Wo(),t=ol(e,t,n);else{if((o=(a=t.stateNode).hydrate)&&(Uo=Wr(t.stateNode.containerInfo.firstChild),zo=t,o=qo=!0),o){if(null!=(e=a.mutableSourceEagerHydrationData))for(a=0;a<e.length;a+=2)(o=e[a])._workInProgressVersionPrimary=e[a+1],Yo.push(o);for(n=Lo(t,null,r,n),t.child=n;n;)n.flags=-3&n.flags|1024,n=n.sibling}else Fi(e,t,r,n),Wo();t=t.child}return t;case 5:return Mo(t),null===e&&Ho(t),r=t.type,a=t.pendingProps,o=null!==e?e.memoizedProps:null,l=a.children,$r(r,a)?l=null:null!==o&&$r(r,o)&&(t.flags|=16),Gi(e,t),Fi(e,t,l,n),t.child;case 6:return null===e&&Ho(t),null;case 13:return Xi(e,t,n);case 4:return Do(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=To(t,null,r,n):Fi(e,t,r,n),t.child;case 11:return r=t.type,a=t.pendingProps,Bi(e,t,r,a=t.elementType===r?a:Qa(r,a),n);case 7:return Fi(e,t,t.pendingProps,n),t.child;case 8:case 12:return Fi(e,t,t.pendingProps.children,n),t.child;case 10:e:{r=t.type._context,a=t.pendingProps,l=t.memoizedProps,o=a.value;var s=t.type._context;if(da(Xa,s._currentValue),s._currentValue=o,null!==l)if(s=l.value,0===(o=ur(s,o)?0:0|("function"==typeof r._calculateChangedBits?r._calculateChangedBits(s,o):1073741823))){if(l.children===a.children&&!ma.current){t=ol(e,t,n);break e}}else for(null!==(s=t.child)&&(s.return=t);null!==s;){var c=s.dependencies;if(null!==c){l=s.child;for(var u=c.firstContext;null!==u;){if(u.context===r&&0!=(u.observedBits&o)){1===s.tag&&((u=uo(-1,n&-n)).tag=2,fo(s,u)),s.lanes|=n,null!==(u=s.alternate)&&(u.lanes|=n),ao(s.return,n),c.lanes|=n;break}u=u.next}}else l=10===s.tag&&s.type===t.type?null:s.child;if(null!==l)l.return=s;else for(l=s;null!==l;){if(l===t){l=null;break}if(null!==(s=l.sibling)){s.return=l.return,l=s;break}l=l.return}s=l}Fi(e,t,a.children,n),t=t.child}return t;case 9:return a=t.type,r=(o=t.pendingProps).children,oo(t,n),r=r(a=io(a,o.unstable_observedBits)),t.flags|=1,Fi(e,t,r,n),t.child;case 14:return o=Qa(a=t.type,t.pendingProps),zi(e,t,a,o=Qa(a.type,o),r,n);case 15:return Ui(e,t,t.type,t.pendingProps,r,n);case 17:return r=t.type,a=t.pendingProps,a=t.elementType===r?a:Qa(r,a),null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2),t.tag=1,va(r)?(e=!0,ka(t)):e=!1,oo(t,n),wo(t,r,a),Eo(t,r,a,n),Zi(null,t,r,!0,e,n);case 19:return al(e,t,n);case 23:case 24:return qi(e,t,n)}throw Error(i(156,t.tag))},ac.prototype.render=function(e){ec(e,this._internalRoot,null,null)},ac.prototype.unmount=function(){var e=this._internalRoot,t=e.containerInfo;ec(null,e,null,(function(){t[ea]=null}))},tt=function(e){13===e.tag&&(ms(e,4,fs()),rc(e,4))},nt=function(e){13===e.tag&&(ms(e,67108864,fs()),rc(e,67108864))},rt=function(e){if(13===e.tag){var t=fs(),n=ps(e);ms(e,n,t),rc(e,n)}},at=function(e,t){return t()},Te=function(e,t,n){switch(t){case"input":if(ne(e,n),t=n.name,"radio"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),t=0;t<n.length;t++){var r=n[t];if(r!==e&&r.form===e.form){var a=oa(r);if(!a)throw Error(i(90));Q(r),ne(r,a)}}}break;case"textarea":ce(e,n);break;case"select":null!=(t=n.value)&&ie(e,!!n.multiple,t,!1)}},Oe=ws,De=function(e,t,n,r,a){var o=Nl;Nl|=4;try{return Za(98,e.bind(null,t,n,r,a))}finally{0===(Nl=o)&&(Vl(),Wa())}},Ie=function(){0==(49&Nl)&&(function(){if(null!==as){var e=as;as=null,e.forEach((function(e){e.expiredLanes|=24&e.pendingLanes,gs(e,Ga())}))}Wa()}(),Is())},Me=function(e,t){var n=Nl;Nl|=2;try{return e(t)}finally{0===(Nl=n)&&(Vl(),Wa())}};var sc={Events:[ra,aa,oa,Re,Ne,Is,{current:!1}]},cc={findFiberByHostInstance:na,bundleType:0,version:"17.0.2",rendererPackageName:"react-dom"},uc={bundleType:cc.bundleType,version:cc.version,rendererPackageName:cc.rendererPackageName,rendererConfig:cc.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:k.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=Je(e))?null:e.stateNode},findFiberByHostInstance:cc.findFiberByHostInstance||function(){return null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null};if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__){var dc=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!dc.isDisabled&&dc.supportsFiber)try{Sa=dc.inject(uc),xa=dc}catch(ge){}}t.hydrate=function(e,t,n){if(!oc(t))throw Error(i(200));return ic(null,e,t,!0,n)}},8316:(e,t,n)=>{"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}(),e.exports=n(2967)},8435:e=>{var t="undefined"!=typeof Element,n="function"==typeof Map,r="function"==typeof Set,a="function"==typeof ArrayBuffer&&!!ArrayBuffer.isView;function o(e,i){if(e===i)return!0;if(e&&i&&"object"==typeof e&&"object"==typeof i){if(e.constructor!==i.constructor)return!1;var l,s,c,u;if(Array.isArray(e)){if((l=e.length)!=i.length)return!1;for(s=l;0!=s--;)if(!o(e[s],i[s]))return!1;return!0}if(n&&e instanceof Map&&i instanceof Map){if(e.size!==i.size)return!1;for(u=e.entries();!(s=u.next()).done;)if(!i.has(s.value[0]))return!1;for(u=e.entries();!(s=u.next()).done;)if(!o(s.value[1],i.get(s.value[0])))return!1;return!0}if(r&&e instanceof Set&&i instanceof Set){if(e.size!==i.size)return!1;for(u=e.entries();!(s=u.next()).done;)if(!i.has(s.value[0]))return!1;return!0}if(a&&ArrayBuffer.isView(e)&&ArrayBuffer.isView(i)){if((l=e.length)!=i.length)return!1;for(s=l;0!=s--;)if(e[s]!==i[s])return!1;return!0}if(e.constructor===RegExp)return e.source===i.source&&e.flags===i.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===i.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===i.toString();if((l=(c=Object.keys(e)).length)!==Object.keys(i).length)return!1;for(s=l;0!=s--;)if(!Object.prototype.hasOwnProperty.call(i,c[s]))return!1;if(t&&e instanceof Element)return!1;for(s=l;0!=s--;)if(("_owner"!==c[s]&&"__v"!==c[s]&&"__o"!==c[s]||!e.$$typeof)&&!o(e[c[s]],i[c[s]]))return!1;return!0}return e!=e&&i!=i}e.exports=function(e,t){try{return o(e,t)}catch(n){if((n.message||"").match(/stack|recursion/i))return console.warn("react-fast-compare cannot handle circular refs"),!1;throw n}}},2175:(e,t,n)=>{"use strict";n.d(t,{B6:()=>H,ql:()=>J});var r=n(2784),a=n(3980),o=n.n(a),i=n(8435),l=n.n(i),s=n(7677),c=n.n(s),u=n(8665),d=n.n(u);function f(){return f=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},f.apply(this,arguments)}function p(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,m(e,t)}function m(e,t){return m=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},m(e,t)}function h(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)t.indexOf(n=o[r])>=0||(a[n]=e[n]);return a}var g={BASE:"base",BODY:"body",HEAD:"head",HTML:"html",LINK:"link",META:"meta",NOSCRIPT:"noscript",SCRIPT:"script",STYLE:"style",TITLE:"title",FRAGMENT:"Symbol(react.fragment)"},v={rel:["amphtml","canonical","alternate"]},b={type:["application/ld+json"]},y={charset:"",name:["robots","description"],property:["og:type","og:title","og:url","og:image","og:image:alt","og:description","twitter:url","twitter:title","twitter:description","twitter:image","twitter:image:alt","twitter:card","twitter:site"]},w=Object.keys(g).map((function(e){return g[e]})),k={accesskey:"accessKey",charset:"charSet",class:"className",contenteditable:"contentEditable",contextmenu:"contextMenu","http-equiv":"httpEquiv",itemprop:"itemProp",tabindex:"tabIndex"},E=Object.keys(k).reduce((function(e,t){return e[k[t]]=t,e}),{}),S=function(e,t){for(var n=e.length-1;n>=0;n-=1){var r=e[n];if(Object.prototype.hasOwnProperty.call(r,t))return r[t]}return null},x=function(e){var t=S(e,g.TITLE),n=S(e,"titleTemplate");if(Array.isArray(t)&&(t=t.join("")),n&&t)return n.replace(/%s/g,(function(){return t}));var r=S(e,"defaultTitle");return t||r||void 0},_=function(e){return S(e,"onChangeClientState")||function(){}},C=function(e,t){return t.filter((function(t){return void 0!==t[e]})).map((function(t){return t[e]})).reduce((function(e,t){return f({},e,t)}),{})},T=function(e,t){return t.filter((function(e){return void 0!==e[g.BASE]})).map((function(e){return e[g.BASE]})).reverse().reduce((function(t,n){if(!t.length)for(var r=Object.keys(n),a=0;a<r.length;a+=1){var o=r[a].toLowerCase();if(-1!==e.indexOf(o)&&n[o])return t.concat(n)}return t}),[])},L=function(e,t,n){var r={};return n.filter((function(t){return!!Array.isArray(t[e])||(void 0!==t[e]&&console&&"function"==typeof console.warn&&console.warn("Helmet: "+e+' should be of type "Array". Instead found type "'+typeof t[e]+'"'),!1)})).map((function(t){return t[e]})).reverse().reduce((function(e,n){var a={};n.filter((function(e){for(var n,o=Object.keys(e),i=0;i<o.length;i+=1){var l=o[i],s=l.toLowerCase();-1===t.indexOf(s)||"rel"===n&&"canonical"===e[n].toLowerCase()||"rel"===s&&"stylesheet"===e[s].toLowerCase()||(n=s),-1===t.indexOf(l)||"innerHTML"!==l&&"cssText"!==l&&"itemprop"!==l||(n=l)}if(!n||!e[n])return!1;var c=e[n].toLowerCase();return r[n]||(r[n]={}),a[n]||(a[n]={}),!r[n][c]&&(a[n][c]=!0,!0)})).reverse().forEach((function(t){return e.push(t)}));for(var o=Object.keys(a),i=0;i<o.length;i+=1){var l=o[i],s=f({},r[l],a[l]);r[l]=s}return e}),[]).reverse()},A=function(e,t){if(Array.isArray(e)&&e.length)for(var n=0;n<e.length;n+=1)if(e[n][t])return!0;return!1},P=function(e){return Array.isArray(e)?e.join(""):e},R=function(e,t){return Array.isArray(e)?e.reduce((function(e,n){return function(e,t){for(var n=Object.keys(e),r=0;r<n.length;r+=1)if(t[n[r]]&&t[n[r]].includes(e[n[r]]))return!0;return!1}(n,t)?e.priority.push(n):e.default.push(n),e}),{priority:[],default:[]}):{default:e}},N=function(e,t){var n;return f({},e,((n={})[t]=void 0,n))},O=[g.NOSCRIPT,g.SCRIPT,g.STYLE],D=function(e,t){return void 0===t&&(t=!0),!1===t?String(e):String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")},I=function(e){return Object.keys(e).reduce((function(t,n){var r=void 0!==e[n]?n+'="'+e[n]+'"':""+n;return t?t+" "+r:r}),"")},M=function(e,t){return void 0===t&&(t={}),Object.keys(e).reduce((function(t,n){return t[k[n]||n]=e[n],t}),t)},j=function(e,t){return t.map((function(t,n){var a,o=((a={key:n})["data-rh"]=!0,a);return Object.keys(t).forEach((function(e){var n=k[e]||e;"innerHTML"===n||"cssText"===n?o.dangerouslySetInnerHTML={__html:t.innerHTML||t.cssText}:o[n]=t[e]})),r.createElement(e,o)}))},F=function(e,t,n){switch(e){case g.TITLE:return{toComponent:function(){return n=t.titleAttributes,(a={key:e=t.title})["data-rh"]=!0,o=M(n,a),[r.createElement(g.TITLE,o,e)];var e,n,a,o},toString:function(){return function(e,t,n,r){var a=I(n),o=P(t);return a?"<"+e+' data-rh="true" '+a+">"+D(o,r)+"</"+e+">":"<"+e+' data-rh="true">'+D(o,r)+"</"+e+">"}(e,t.title,t.titleAttributes,n)}};case"bodyAttributes":case"htmlAttributes":return{toComponent:function(){return M(t)},toString:function(){return I(t)}};default:return{toComponent:function(){return j(e,t)},toString:function(){return function(e,t,n){return t.reduce((function(t,r){var a=Object.keys(r).filter((function(e){return!("innerHTML"===e||"cssText"===e)})).reduce((function(e,t){var a=void 0===r[t]?t:t+'="'+D(r[t],n)+'"';return e?e+" "+a:a}),""),o=r.innerHTML||r.cssText||"",i=-1===O.indexOf(e);return t+"<"+e+' data-rh="true" '+a+(i?"/>":">"+o+"</"+e+">")}),"")}(e,t,n)}}}},B=function(e){var t=e.baseTag,n=e.bodyAttributes,r=e.encode,a=e.htmlAttributes,o=e.noscriptTags,i=e.styleTags,l=e.title,s=void 0===l?"":l,c=e.titleAttributes,u=e.linkTags,d=e.metaTags,f=e.scriptTags,p={toComponent:function(){},toString:function(){return""}};if(e.prioritizeSeoTags){var m=function(e){var t=e.linkTags,n=e.scriptTags,r=e.encode,a=R(e.metaTags,y),o=R(t,v),i=R(n,b);return{priorityMethods:{toComponent:function(){return[].concat(j(g.META,a.priority),j(g.LINK,o.priority),j(g.SCRIPT,i.priority))},toString:function(){return F(g.META,a.priority,r)+" "+F(g.LINK,o.priority,r)+" "+F(g.SCRIPT,i.priority,r)}},metaTags:a.default,linkTags:o.default,scriptTags:i.default}}(e);p=m.priorityMethods,u=m.linkTags,d=m.metaTags,f=m.scriptTags}return{priority:p,base:F(g.BASE,t,r),bodyAttributes:F("bodyAttributes",n,r),htmlAttributes:F("htmlAttributes",a,r),link:F(g.LINK,u,r),meta:F(g.META,d,r),noscript:F(g.NOSCRIPT,o,r),script:F(g.SCRIPT,f,r),style:F(g.STYLE,i,r),title:F(g.TITLE,{title:s,titleAttributes:c},r)}},z=[],U=function(e,t){var n=this;void 0===t&&(t="undefined"!=typeof document),this.instances=[],this.value={setHelmet:function(e){n.context.helmet=e},helmetInstances:{get:function(){return n.canUseDOM?z:n.instances},add:function(e){(n.canUseDOM?z:n.instances).push(e)},remove:function(e){var t=(n.canUseDOM?z:n.instances).indexOf(e);(n.canUseDOM?z:n.instances).splice(t,1)}}},this.context=e,this.canUseDOM=t,t||(e.helmet=B({baseTag:[],bodyAttributes:{},encodeSpecialCharacters:!0,htmlAttributes:{},linkTags:[],metaTags:[],noscriptTags:[],scriptTags:[],styleTags:[],title:"",titleAttributes:{}}))},q=r.createContext({}),G=o().shape({setHelmet:o().func,helmetInstances:o().shape({get:o().func,add:o().func,remove:o().func})}),$="undefined"!=typeof document,H=function(e){function t(n){var r;return(r=e.call(this,n)||this).helmetData=new U(r.props.context,t.canUseDOM),r}return p(t,e),t.prototype.render=function(){return r.createElement(q.Provider,{value:this.helmetData.value},this.props.children)},t}(r.Component);H.canUseDOM=$,H.propTypes={context:o().shape({helmet:o().shape()}),children:o().node.isRequired},H.defaultProps={context:{}},H.displayName="HelmetProvider";var Z=function(e,t){var n,r=document.head||document.querySelector(g.HEAD),a=r.querySelectorAll(e+"[data-rh]"),o=[].slice.call(a),i=[];return t&&t.length&&t.forEach((function(t){var r=document.createElement(e);for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&("innerHTML"===a?r.innerHTML=t.innerHTML:"cssText"===a?r.styleSheet?r.styleSheet.cssText=t.cssText:r.appendChild(document.createTextNode(t.cssText)):r.setAttribute(a,void 0===t[a]?"":t[a]));r.setAttribute("data-rh","true"),o.some((function(e,t){return n=t,r.isEqualNode(e)}))?o.splice(n,1):i.push(r)})),o.forEach((function(e){return e.parentNode.removeChild(e)})),i.forEach((function(e){return r.appendChild(e)})),{oldTags:o,newTags:i}},V=function(e,t){var n=document.getElementsByTagName(e)[0];if(n){for(var r=n.getAttribute("data-rh"),a=r?r.split(","):[],o=[].concat(a),i=Object.keys(t),l=0;l<i.length;l+=1){var s=i[l],c=t[s]||"";n.getAttribute(s)!==c&&n.setAttribute(s,c),-1===a.indexOf(s)&&a.push(s);var u=o.indexOf(s);-1!==u&&o.splice(u,1)}for(var d=o.length-1;d>=0;d-=1)n.removeAttribute(o[d]);a.length===o.length?n.removeAttribute("data-rh"):n.getAttribute("data-rh")!==i.join(",")&&n.setAttribute("data-rh",i.join(","))}},W=function(e,t){var n=e.baseTag,r=e.htmlAttributes,a=e.linkTags,o=e.metaTags,i=e.noscriptTags,l=e.onChangeClientState,s=e.scriptTags,c=e.styleTags,u=e.title,d=e.titleAttributes;V(g.BODY,e.bodyAttributes),V(g.HTML,r),function(e,t){void 0!==e&&document.title!==e&&(document.title=P(e)),V(g.TITLE,t)}(u,d);var f={baseTag:Z(g.BASE,n),linkTags:Z(g.LINK,a),metaTags:Z(g.META,o),noscriptTags:Z(g.NOSCRIPT,i),scriptTags:Z(g.SCRIPT,s),styleTags:Z(g.STYLE,c)},p={},m={};Object.keys(f).forEach((function(e){var t=f[e],n=t.newTags,r=t.oldTags;n.length&&(p[e]=n),r.length&&(m[e]=f[e].oldTags)})),t&&t(),l(e,p,m)},Y=null,K=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).rendered=!1,t}p(t,e);var n=t.prototype;return n.shouldComponentUpdate=function(e){return!d()(e,this.props)},n.componentDidUpdate=function(){this.emitChange()},n.componentWillUnmount=function(){this.props.context.helmetInstances.remove(this),this.emitChange()},n.emitChange=function(){var e,t,n=this.props.context,r=n.setHelmet,a=null,o=(e=n.helmetInstances.get().map((function(e){var t=f({},e.props);return delete t.context,t})),{baseTag:T(["href"],e),bodyAttributes:C("bodyAttributes",e),defer:S(e,"defer"),encode:S(e,"encodeSpecialCharacters"),htmlAttributes:C("htmlAttributes",e),linkTags:L(g.LINK,["rel","href"],e),metaTags:L(g.META,["name","charset","http-equiv","property","itemprop"],e),noscriptTags:L(g.NOSCRIPT,["innerHTML"],e),onChangeClientState:_(e),scriptTags:L(g.SCRIPT,["src","innerHTML"],e),styleTags:L(g.STYLE,["cssText"],e),title:x(e),titleAttributes:C("titleAttributes",e),prioritizeSeoTags:A(e,"prioritizeSeoTags")});H.canUseDOM?(t=o,Y&&cancelAnimationFrame(Y),t.defer?Y=requestAnimationFrame((function(){W(t,(function(){Y=null}))})):(W(t),Y=null)):B&&(a=B(o)),r(a)},n.init=function(){this.rendered||(this.rendered=!0,this.props.context.helmetInstances.add(this),this.emitChange())},n.render=function(){return this.init(),null},t}(r.Component);K.propTypes={context:G.isRequired},K.displayName="HelmetDispatcher";var Q=["children"],X=["children"],J=function(e){function t(){return e.apply(this,arguments)||this}p(t,e);var n=t.prototype;return n.shouldComponentUpdate=function(e){return!l()(N(this.props,"helmetData"),N(e,"helmetData"))},n.mapNestedChildrenToProps=function(e,t){if(!t)return null;switch(e.type){case g.SCRIPT:case g.NOSCRIPT:return{innerHTML:t};case g.STYLE:return{cssText:t};default:throw new Error("<"+e.type+" /> elements are self-closing and can not contain children. Refer to our API for more information.")}},n.flattenArrayTypeChildren=function(e){var t,n=e.child,r=e.arrayTypeChildren;return f({},r,((t={})[n.type]=[].concat(r[n.type]||[],[f({},e.newChildProps,this.mapNestedChildrenToProps(n,e.nestedChildren))]),t))},n.mapObjectTypeChildren=function(e){var t,n,r=e.child,a=e.newProps,o=e.newChildProps,i=e.nestedChildren;switch(r.type){case g.TITLE:return f({},a,((t={})[r.type]=i,t.titleAttributes=f({},o),t));case g.BODY:return f({},a,{bodyAttributes:f({},o)});case g.HTML:return f({},a,{htmlAttributes:f({},o)});default:return f({},a,((n={})[r.type]=f({},o),n))}},n.mapArrayTypeChildrenToProps=function(e,t){var n=f({},t);return Object.keys(e).forEach((function(t){var r;n=f({},n,((r={})[t]=e[t],r))})),n},n.warnOnInvalidChildren=function(e,t){return c()(w.some((function(t){return e.type===t})),"function"==typeof e.type?"You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.":"Only elements types "+w.join(", ")+" are allowed. Helmet does not support rendering <"+e.type+"> elements. Refer to our API for more information."),c()(!t||"string"==typeof t||Array.isArray(t)&&!t.some((function(e){return"string"!=typeof e})),"Helmet expects a string as a child of <"+e.type+">. Did you forget to wrap your children in braces? ( <"+e.type+">{``}</"+e.type+"> ) Refer to our API for more information."),!0},n.mapChildrenToProps=function(e,t){var n=this,a={};return r.Children.forEach(e,(function(e){if(e&&e.props){var r=e.props,o=r.children,i=h(r,Q),l=Object.keys(i).reduce((function(e,t){return e[E[t]||t]=i[t],e}),{}),s=e.type;switch("symbol"==typeof s?s=s.toString():n.warnOnInvalidChildren(e,o),s){case g.FRAGMENT:t=n.mapChildrenToProps(o,t);break;case g.LINK:case g.META:case g.NOSCRIPT:case g.SCRIPT:case g.STYLE:a=n.flattenArrayTypeChildren({child:e,arrayTypeChildren:a,newChildProps:l,nestedChildren:o});break;default:t=n.mapObjectTypeChildren({child:e,newProps:t,newChildProps:l,nestedChildren:o})}}})),this.mapArrayTypeChildrenToProps(a,t)},n.render=function(){var e=this.props,t=e.children,n=h(e,X),a=f({},n),o=n.helmetData;return t&&(a=this.mapChildrenToProps(t,a)),!o||o instanceof U||(o=new U(o.context,o.instances)),o?r.createElement(K,f({},a,{context:o.value,helmetData:void 0})):r.createElement(q.Consumer,null,(function(e){return r.createElement(K,f({},a,{context:e}))}))},t}(r.Component);J.propTypes={base:o().object,bodyAttributes:o().object,children:o().oneOfType([o().arrayOf(o().node),o().node]),defaultTitle:o().string,defer:o().bool,encodeSpecialCharacters:o().bool,htmlAttributes:o().object,link:o().arrayOf(o().object),meta:o().arrayOf(o().object),noscript:o().arrayOf(o().object),onChangeClientState:o().func,script:o().arrayOf(o().object),style:o().arrayOf(o().object),title:o().string,titleAttributes:o().object,titleTemplate:o().string,prioritizeSeoTags:o().bool,helmetData:o().object},J.defaultProps={defer:!0,encodeSpecialCharacters:!0,prioritizeSeoTags:!1},J.displayName="Helmet"},9028:(e,t,n)=>{"use strict";function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function a(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(){return i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},i.apply(this,arguments)}var l=n(2784),s=n(3980),c=[],u=[];function d(e){var t=e(),n={loading:!0,loaded:null,error:null};return n.promise=t.then((function(e){return n.loading=!1,n.loaded=e,e})).catch((function(e){throw n.loading=!1,n.error=e,e})),n}function f(e){var t={loading:!1,loaded:{},error:null},n=[];try{Object.keys(e).forEach((function(r){var a=d(e[r]);a.loading?t.loading=!0:(t.loaded[r]=a.loaded,t.error=a.error),n.push(a.promise),a.promise.then((function(e){t.loaded[r]=e})).catch((function(e){t.error=e}))}))}catch(r){t.error=r}return t.promise=Promise.all(n).then((function(e){return t.loading=!1,e})).catch((function(e){throw t.loading=!1,e})),t}function p(e,t){return l.createElement((n=e)&&n.__esModule?n.default:n,t);var n}function m(e,t){var d,f;if(!t.loading)throw new Error("react-loadable requires a `loading` component");var m=i({loader:null,loading:null,delay:200,timeout:null,render:p,webpack:null,modules:null},t),h=null;function g(){return h||(h=e(m.loader)),h.promise}return c.push(g),"function"==typeof m.webpack&&u.push((function(){if((0,m.webpack)().every((function(e){return void 0!==e&&void 0!==n.m[e]})))return g()})),f=d=function(t){function n(n){var r;return o(a(a(r=t.call(this,n)||this)),"retry",(function(){r.setState({error:null,loading:!0,timedOut:!1}),h=e(m.loader),r._loadModule()})),g(),r.state={error:h.error,pastDelay:!1,timedOut:!1,loading:h.loading,loaded:h.loaded},r}r(n,t),n.preload=function(){return g()};var i=n.prototype;return i.UNSAFE_componentWillMount=function(){this._loadModule()},i.componentDidMount=function(){this._mounted=!0},i._loadModule=function(){var e=this;if(this.context.loadable&&Array.isArray(m.modules)&&m.modules.forEach((function(t){e.context.loadable.report(t)})),h.loading){var t=function(t){e._mounted&&e.setState(t)};"number"==typeof m.delay&&(0===m.delay?this.setState({pastDelay:!0}):this._delay=setTimeout((function(){t({pastDelay:!0})}),m.delay)),"number"==typeof m.timeout&&(this._timeout=setTimeout((function(){t({timedOut:!0})}),m.timeout));var n=function(){t({error:h.error,loaded:h.loaded,loading:h.loading}),e._clearTimeouts()};h.promise.then((function(){return n(),null})).catch((function(e){return n(),null}))}},i.componentWillUnmount=function(){this._mounted=!1,this._clearTimeouts()},i._clearTimeouts=function(){clearTimeout(this._delay),clearTimeout(this._timeout)},i.render=function(){return this.state.loading||this.state.error?l.createElement(m.loading,{isLoading:this.state.loading,pastDelay:this.state.pastDelay,timedOut:this.state.timedOut,error:this.state.error,retry:this.retry}):this.state.loaded?m.render(this.state.loaded,this.props):null},n}(l.Component),o(d,"contextTypes",{loadable:s.shape({report:s.func.isRequired})}),f}function h(e){return m(d,e)}h.Map=function(e){if("function"!=typeof e.render)throw new Error("LoadableMap requires a `render(loaded, props)` function");return m(f,e)};var g=function(e){function t(){return e.apply(this,arguments)||this}r(t,e);var n=t.prototype;return n.getChildContext=function(){return{loadable:{report:this.props.report}}},n.render=function(){return l.Children.only(this.props.children)},t}(l.Component);function v(e){for(var t=[];e.length;){var n=e.pop();t.push(n())}return Promise.all(t).then((function(){if(e.length)return v(e)}))}o(g,"propTypes",{report:s.func.isRequired}),o(g,"childContextTypes",{loadable:s.shape({report:s.func.isRequired}).isRequired}),h.Capture=g,h.preloadAll=function(){return new Promise((function(e,t){v(c).then(e,t)}))},h.preloadReady=function(){return new Promise((function(e,t){v(u).then(e,e)}))},e.exports=h},9702:(e,t,n)=>{"use strict";n.d(t,{H:()=>l,f:()=>i});var r=n(3181),a=n(7896),o=n(2784);function i(e,t,n){return void 0===n&&(n=[]),e.some((function(e){var a=e.path?(0,r.LX)(t,e):n.length?n[n.length-1].match:r.F0.computeRootMatch(t);return a&&(n.push({route:e,match:a}),e.routes&&i(e.routes,t,n)),a})),n}function l(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),e?o.createElement(r.rs,n,e.map((function(e,n){return o.createElement(r.AW,{key:e.key||n,path:e.path,exact:e.exact,strict:e.strict,render:function(n){return e.render?e.render((0,a.Z)({},n,{},t,{route:e})):o.createElement(e.component,(0,a.Z)({},n,t,{route:e}))}})}))):null}},7933:(e,t,n)=>{"use strict";n.d(t,{OL:()=>y,VK:()=>u,rU:()=>g});var r=n(3181),a=n(2222),o=n(2784),i=n(4410),l=n(7896),s=n(1461),c=n(1898),u=function(e){function t(){for(var t,n=arguments.length,r=new Array(n),a=0;a<n;a++)r[a]=arguments[a];return(t=e.call.apply(e,[this].concat(r))||this).history=(0,i.lX)(t.props),t}return(0,a.Z)(t,e),t.prototype.render=function(){return o.createElement(r.F0,{history:this.history,children:this.props.children})},t}(o.Component);o.Component;var d=function(e,t){return"function"==typeof e?e(t):e},f=function(e,t){return"string"==typeof e?(0,i.ob)(e,null,null,t):e},p=function(e){return e},m=o.forwardRef;void 0===m&&(m=p);var h=m((function(e,t){var n=e.innerRef,r=e.navigate,a=e.onClick,i=(0,s.Z)(e,["innerRef","navigate","onClick"]),c=i.target,u=(0,l.Z)({},i,{onClick:function(e){try{a&&a(e)}catch(t){throw e.preventDefault(),t}e.defaultPrevented||0!==e.button||c&&"_self"!==c||function(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e)||(e.preventDefault(),r())}});return u.ref=p!==m&&t||n,o.createElement("a",u)}));var g=m((function(e,t){var n=e.component,a=void 0===n?h:n,u=e.replace,g=e.to,v=e.innerRef,b=(0,s.Z)(e,["component","replace","to","innerRef"]);return o.createElement(r.s6.Consumer,null,(function(e){e||(0,c.Z)(!1);var n=e.history,r=f(d(g,e.location),e.location),s=r?n.createHref(r):"",h=(0,l.Z)({},b,{href:s,navigate:function(){var t=d(g,e.location),r=(0,i.Ep)(e.location)===(0,i.Ep)(f(t));(u||r?n.replace:n.push)(t)}});return p!==m?h.ref=t||v:h.innerRef=v,o.createElement(a,h)}))})),v=function(e){return e},b=o.forwardRef;void 0===b&&(b=v);var y=b((function(e,t){var n=e["aria-current"],a=void 0===n?"page":n,i=e.activeClassName,u=void 0===i?"active":i,p=e.activeStyle,m=e.className,h=e.exact,y=e.isActive,w=e.location,k=e.sensitive,E=e.strict,S=e.style,x=e.to,_=e.innerRef,C=(0,s.Z)(e,["aria-current","activeClassName","activeStyle","className","exact","isActive","location","sensitive","strict","style","to","innerRef"]);return o.createElement(r.s6.Consumer,null,(function(e){e||(0,c.Z)(!1);var n=w||e.location,i=f(d(x,n),n),s=i.pathname,T=s&&s.replace(/([.+*?=^!:${}()[\]|/\\])/g,"\\$1"),L=T?(0,r.LX)(n.pathname,{path:T,exact:h,sensitive:k,strict:E}):null,A=!!(y?y(L,n):L),P="function"==typeof m?m(A):m,R="function"==typeof S?S(A):S;A&&(P=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.filter((function(e){return e})).join(" ")}(P,u),R=(0,l.Z)({},R,p));var N=(0,l.Z)({"aria-current":A&&a||null,className:P,style:R,to:i},C);return v!==b?N.ref=t||_:N.innerRef=_,o.createElement(g,N)}))}))},3181:(e,t,n)=>{"use strict";n.d(t,{AW:()=>_,F0:()=>k,rs:()=>R,s6:()=>w,LX:()=>x,k6:()=>O,TH:()=>D});var r=n(2222),a=n(2784),o=n(4410),i=n(3980),l=n.n(i),s=1073741823,c="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==n.g?n.g:{};function u(e){var t=[];return{on:function(e){t.push(e)},off:function(e){t=t.filter((function(t){return t!==e}))},get:function(){return e},set:function(n,r){e=n,t.forEach((function(t){return t(e,r)}))}}}var d=a.createContext||function(e,t){var n,o,i="__create-react-context-"+function(){var e="__global_unique_id__";return c[e]=(c[e]||0)+1}()+"__",d=function(e){function n(){var t;return(t=e.apply(this,arguments)||this).emitter=u(t.props.value),t}(0,r.Z)(n,e);var a=n.prototype;return a.getChildContext=function(){var e;return(e={})[i]=this.emitter,e},a.componentWillReceiveProps=function(e){if(this.props.value!==e.value){var n,r=this.props.value,a=e.value;((o=r)===(i=a)?0!==o||1/o==1/i:o!=o&&i!=i)?n=0:(n="function"==typeof t?t(r,a):s,0!==(n|=0)&&this.emitter.set(e.value,n))}var o,i},a.render=function(){return this.props.children},n}(a.Component);d.childContextTypes=((n={})[i]=l().object.isRequired,n);var f=function(t){function n(){var e;return(e=t.apply(this,arguments)||this).state={value:e.getValue()},e.onUpdate=function(t,n){0!=((0|e.observedBits)&n)&&e.setState({value:e.getValue()})},e}(0,r.Z)(n,t);var a=n.prototype;return a.componentWillReceiveProps=function(e){var t=e.observedBits;this.observedBits=null==t?s:t},a.componentDidMount=function(){this.context[i]&&this.context[i].on(this.onUpdate);var e=this.props.observedBits;this.observedBits=null==e?s:e},a.componentWillUnmount=function(){this.context[i]&&this.context[i].off(this.onUpdate)},a.getValue=function(){return this.context[i]?this.context[i].get():e},a.render=function(){return(e=this.props.children,Array.isArray(e)?e[0]:e)(this.state.value);var e},n}(a.Component);return f.contextTypes=((o={})[i]=l().object,o),{Provider:d,Consumer:f}};const f=d;var p=n(1898),m=n(7896),h=n(99),g=n.n(h),v=(n(9744),n(1461)),b=(n(3463),function(e){var t=f();return t.displayName=e,t}),y=b("Router-History"),w=b("Router"),k=function(e){function t(t){var n;return(n=e.call(this,t)||this).state={location:t.history.location},n._isMounted=!1,n._pendingLocation=null,t.staticContext||(n.unlisten=t.history.listen((function(e){n._pendingLocation=e}))),n}(0,r.Z)(t,e),t.computeRootMatch=function(e){return{path:"/",url:"/",params:{},isExact:"/"===e}};var n=t.prototype;return n.componentDidMount=function(){var e=this;this._isMounted=!0,this.unlisten&&this.unlisten(),this.props.staticContext||(this.unlisten=this.props.history.listen((function(t){e._isMounted&&e.setState({location:t})}))),this._pendingLocation&&this.setState({location:this._pendingLocation})},n.componentWillUnmount=function(){this.unlisten&&(this.unlisten(),this._isMounted=!1,this._pendingLocation=null)},n.render=function(){return a.createElement(w.Provider,{value:{history:this.props.history,location:this.state.location,match:t.computeRootMatch(this.state.location.pathname),staticContext:this.props.staticContext}},a.createElement(y.Provider,{children:this.props.children||null,value:this.props.history}))},t}(a.Component);a.Component;a.Component;var E={},S=0;function x(e,t){void 0===t&&(t={}),("string"==typeof t||Array.isArray(t))&&(t={path:t});var n=t,r=n.path,a=n.exact,o=void 0!==a&&a,i=n.strict,l=void 0!==i&&i,s=n.sensitive,c=void 0!==s&&s;return[].concat(r).reduce((function(t,n){if(!n&&""!==n)return null;if(t)return t;var r=function(e,t){var n=""+t.end+t.strict+t.sensitive,r=E[n]||(E[n]={});if(r[e])return r[e];var a=[],o={regexp:g()(e,a,t),keys:a};return S<1e4&&(r[e]=o,S++),o}(n,{end:o,strict:l,sensitive:c}),a=r.regexp,i=r.keys,s=a.exec(e);if(!s)return null;var u=s[0],d=s.slice(1),f=e===u;return o&&!f?null:{path:n,url:"/"===n&&""===u?"/":u,isExact:f,params:i.reduce((function(e,t,n){return e[t.name]=d[n],e}),{})}}),null)}var _=function(e){function t(){return e.apply(this,arguments)||this}return(0,r.Z)(t,e),t.prototype.render=function(){var e=this;return a.createElement(w.Consumer,null,(function(t){t||(0,p.Z)(!1);var n=e.props.location||t.location,r=e.props.computedMatch?e.props.computedMatch:e.props.path?x(n.pathname,e.props):t.match,o=(0,m.Z)({},t,{location:n,match:r}),i=e.props,l=i.children,s=i.component,c=i.render;return Array.isArray(l)&&function(e){return 0===a.Children.count(e)}(l)&&(l=null),a.createElement(w.Provider,{value:o},o.match?l?"function"==typeof l?l(o):l:s?a.createElement(s,o):c?c(o):null:"function"==typeof l?l(o):null)}))},t}(a.Component);function C(e){return"/"===e.charAt(0)?e:"/"+e}function T(e,t){if(!e)return t;var n=C(e);return 0!==t.pathname.indexOf(n)?t:(0,m.Z)({},t,{pathname:t.pathname.substr(n.length)})}function L(e){return"string"==typeof e?e:(0,o.Ep)(e)}function A(e){return function(){(0,p.Z)(!1)}}function P(){}a.Component;var R=function(e){function t(){return e.apply(this,arguments)||this}return(0,r.Z)(t,e),t.prototype.render=function(){var e=this;return a.createElement(w.Consumer,null,(function(t){t||(0,p.Z)(!1);var n,r,o=e.props.location||t.location;return a.Children.forEach(e.props.children,(function(e){if(null==r&&a.isValidElement(e)){n=e;var i=e.props.path||e.props.from;r=i?x(o.pathname,(0,m.Z)({},e.props,{path:i})):t.match}})),r?a.cloneElement(n,{location:o,computedMatch:r}):null}))},t}(a.Component);var N=a.useContext;function O(){return N(y)}function D(){return N(w).location}},1897:(e,t)=>{"use strict";var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,a=n?Symbol.for("react.portal"):60106,o=n?Symbol.for("react.fragment"):60107,i=n?Symbol.for("react.strict_mode"):60108,l=n?Symbol.for("react.profiler"):60114,s=n?Symbol.for("react.provider"):60109,c=n?Symbol.for("react.context"):60110,u=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,f=n?Symbol.for("react.forward_ref"):60112,p=n?Symbol.for("react.suspense"):60113,m=n?Symbol.for("react.suspense_list"):60120,h=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,v=n?Symbol.for("react.block"):60121,b=n?Symbol.for("react.fundamental"):60117,y=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function k(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case u:case d:case o:case l:case i:case p:return e;default:switch(e=e&&e.$$typeof){case c:case f:case g:case h:case s:return e;default:return t}}case a:return t}}}function E(e){return k(e)===d}},9744:(e,t,n)=>{"use strict";n(1897)},3426:(e,t,n)=>{"use strict";var r=n(7320),a=60103,o=60106;t.Fragment=60107,t.StrictMode=60108,t.Profiler=60114;var i=60109,l=60110,s=60112;t.Suspense=60113;var c=60115,u=60116;if("function"==typeof Symbol&&Symbol.for){var d=Symbol.for;a=d("react.element"),o=d("react.portal"),t.Fragment=d("react.fragment"),t.StrictMode=d("react.strict_mode"),t.Profiler=d("react.profiler"),i=d("react.provider"),l=d("react.context"),s=d("react.forward_ref"),t.Suspense=d("react.suspense"),c=d("react.memo"),u=d("react.lazy")}var f="function"==typeof Symbol&&Symbol.iterator;function p(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h={};function g(e,t,n){this.props=e,this.context=t,this.refs=h,this.updater=n||m}function v(){}function b(e,t,n){this.props=e,this.context=t,this.refs=h,this.updater=n||m}g.prototype.isReactComponent={},g.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error(p(85));this.updater.enqueueSetState(this,e,t,"setState")},g.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},v.prototype=g.prototype;var y=b.prototype=new v;y.constructor=b,r(y,g.prototype),y.isPureReactComponent=!0;var w={current:null},k=Object.prototype.hasOwnProperty,E={key:!0,ref:!0,__self:!0,__source:!0};function S(e,t,n){var r,o={},i=null,l=null;if(null!=t)for(r in void 0!==t.ref&&(l=t.ref),void 0!==t.key&&(i=""+t.key),t)k.call(t,r)&&!E.hasOwnProperty(r)&&(o[r]=t[r]);var s=arguments.length-2;if(1===s)o.children=n;else if(1<s){for(var c=Array(s),u=0;u<s;u++)c[u]=arguments[u+2];o.children=c}if(e&&e.defaultProps)for(r in s=e.defaultProps)void 0===o[r]&&(o[r]=s[r]);return{$$typeof:a,type:e,key:i,ref:l,props:o,_owner:w.current}}function x(e){return"object"==typeof e&&null!==e&&e.$$typeof===a}var _=/\/+/g;function C(e,t){return"object"==typeof e&&null!==e&&null!=e.key?function(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,(function(e){return t[e]}))}(""+e.key):t.toString(36)}function T(e,t,n,r,i){var l=typeof e;"undefined"!==l&&"boolean"!==l||(e=null);var s=!1;if(null===e)s=!0;else switch(l){case"string":case"number":s=!0;break;case"object":switch(e.$$typeof){case a:case o:s=!0}}if(s)return i=i(s=e),e=""===r?"."+C(s,0):r,Array.isArray(i)?(n="",null!=e&&(n=e.replace(_,"$&/")+"/"),T(i,t,n,"",(function(e){return e}))):null!=i&&(x(i)&&(i=function(e,t){return{$$typeof:a,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}(i,n+(!i.key||s&&s.key===i.key?"":(""+i.key).replace(_,"$&/")+"/")+e)),t.push(i)),1;if(s=0,r=""===r?".":r+":",Array.isArray(e))for(var c=0;c<e.length;c++){var u=r+C(l=e[c],c);s+=T(l,t,n,u,i)}else if(u=function(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=f&&e[f]||e["@@iterator"])?e:null}(e),"function"==typeof u)for(e=u.call(e),c=0;!(l=e.next()).done;)s+=T(l=l.value,t,n,u=r+C(l,c++),i);else if("object"===l)throw t=""+e,Error(p(31,"[object Object]"===t?"object with keys {"+Object.keys(e).join(", ")+"}":t));return s}function L(e,t,n){if(null==e)return e;var r=[],a=0;return T(e,r,"","",(function(e){return t.call(n,e,a++)})),r}function A(e){if(-1===e._status){var t=e._result;t=t(),e._status=0,e._result=t,t.then((function(t){0===e._status&&(t=t.default,e._status=1,e._result=t)}),(function(t){0===e._status&&(e._status=2,e._result=t)}))}if(1===e._status)return e._result;throw e._result}var P={current:null};function R(){var e=P.current;if(null===e)throw Error(p(321));return e}var N={ReactCurrentDispatcher:P,ReactCurrentBatchConfig:{transition:0},ReactCurrentOwner:w,IsSomeRendererActing:{current:!1},assign:r};t.Children={map:L,forEach:function(e,t,n){L(e,(function(){t.apply(this,arguments)}),n)},count:function(e){var t=0;return L(e,(function(){t++})),t},toArray:function(e){return L(e,(function(e){return e}))||[]},only:function(e){if(!x(e))throw Error(p(143));return e}},t.Component=g,t.PureComponent=b,t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=N,t.cloneElement=function(e,t,n){if(null==e)throw Error(p(267,e));var o=r({},e.props),i=e.key,l=e.ref,s=e._owner;if(null!=t){if(void 0!==t.ref&&(l=t.ref,s=w.current),void 0!==t.key&&(i=""+t.key),e.type&&e.type.defaultProps)var c=e.type.defaultProps;for(u in t)k.call(t,u)&&!E.hasOwnProperty(u)&&(o[u]=void 0===t[u]&&void 0!==c?c[u]:t[u])}var u=arguments.length-2;if(1===u)o.children=n;else if(1<u){c=Array(u);for(var d=0;d<u;d++)c[d]=arguments[d+2];o.children=c}return{$$typeof:a,type:e.type,key:i,ref:l,props:o,_owner:s}},t.createContext=function(e,t){return void 0===t&&(t=null),(e={$$typeof:l,_calculateChangedBits:t,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null}).Provider={$$typeof:i,_context:e},e.Consumer=e},t.createElement=S,t.createFactory=function(e){var t=S.bind(null,e);return t.type=e,t},t.createRef=function(){return{current:null}},t.forwardRef=function(e){return{$$typeof:s,render:e}},t.isValidElement=x,t.lazy=function(e){return{$$typeof:u,_payload:{_status:-1,_result:e},_init:A}},t.memo=function(e,t){return{$$typeof:c,type:e,compare:void 0===t?null:t}},t.useCallback=function(e,t){return R().useCallback(e,t)},t.useContext=function(e,t){return R().useContext(e,t)},t.useDebugValue=function(){},t.useEffect=function(e,t){return R().useEffect(e,t)},t.useImperativeHandle=function(e,t,n){return R().useImperativeHandle(e,t,n)},t.useLayoutEffect=function(e,t){return R().useLayoutEffect(e,t)},t.useMemo=function(e,t){return R().useMemo(e,t)},t.useReducer=function(e,t,n){return R().useReducer(e,t,n)},t.useRef=function(e){return R().useRef(e)},t.useState=function(e){return R().useState(e)},t.version="17.0.2"},2784:(e,t,n)=>{"use strict";e.exports=n(3426)},6475:(e,t)=>{"use strict";var n,r,a,o;if("object"==typeof performance&&"function"==typeof performance.now){var i=performance;t.unstable_now=function(){return i.now()}}else{var l=Date,s=l.now();t.unstable_now=function(){return l.now()-s}}if("undefined"==typeof window||"function"!=typeof MessageChannel){var c=null,u=null,d=function(){if(null!==c)try{var e=t.unstable_now();c(!0,e),c=null}catch(n){throw setTimeout(d,0),n}};n=function(e){null!==c?setTimeout(n,0,e):(c=e,setTimeout(d,0))},r=function(e,t){u=setTimeout(e,t)},a=function(){clearTimeout(u)},t.unstable_shouldYield=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var f=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var m=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 m&&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 h=!1,g=null,v=-1,b=5,y=0;t.unstable_shouldYield=function(){return t.unstable_now()>=y},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):b=0<e?Math.floor(1e3/e):5};var w=new MessageChannel,k=w.port2;w.port1.onmessage=function(){if(null!==g){var e=t.unstable_now();y=e+b;try{g(!0,e)?k.postMessage(null):(h=!1,g=null)}catch(n){throw k.postMessage(null),n}}else h=!1},n=function(e){g=e,h||(h=!0,k.postMessage(null))},r=function(e,n){v=f((function(){e(t.unstable_now())}),n)},a=function(){p(v),v=-1}}function E(e,t){var n=e.length;e.push(t);e:for(;;){var r=n-1>>>1,a=e[r];if(!(void 0!==a&&0<_(a,t)))break e;e[r]=t,e[n]=a,n=r}}function S(e){return void 0===(e=e[0])?null:e}function x(e){var t=e[0];if(void 0!==t){var n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,a=e.length;r<a;){var o=2*(r+1)-1,i=e[o],l=o+1,s=e[l];if(void 0!==i&&0>_(i,n))void 0!==s&&0>_(s,i)?(e[r]=s,e[l]=n,r=l):(e[r]=i,e[o]=n,r=o);else{if(!(void 0!==s&&0>_(s,n)))break e;e[r]=s,e[l]=n,r=l}}}return t}return null}function _(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var C=[],T=[],L=1,A=null,P=3,R=!1,N=!1,O=!1;function D(e){for(var t=S(T);null!==t;){if(null===t.callback)x(T);else{if(!(t.startTime<=e))break;x(T),t.sortIndex=t.expirationTime,E(C,t)}t=S(T)}}function I(e){if(O=!1,D(e),!N)if(null!==S(C))N=!0,n(M);else{var t=S(T);null!==t&&r(I,t.startTime-e)}}function M(e,n){N=!1,O&&(O=!1,a()),R=!0;var o=P;try{for(D(n),A=S(C);null!==A&&(!(A.expirationTime>n)||e&&!t.unstable_shouldYield());){var i=A.callback;if("function"==typeof i){A.callback=null,P=A.priorityLevel;var l=i(A.expirationTime<=n);n=t.unstable_now(),"function"==typeof l?A.callback=l:A===S(C)&&x(C),D(n)}else x(C);A=S(C)}if(null!==A)var s=!0;else{var c=S(T);null!==c&&r(I,c.startTime-n),s=!1}return s}finally{A=null,P=o,R=!1}}var j=o;t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){N||R||(N=!0,n(M))},t.unstable_getCurrentPriorityLevel=function(){return P},t.unstable_getFirstCallbackNode=function(){return S(C)},t.unstable_next=function(e){switch(P){case 1:case 2:case 3:var t=3;break;default:t=P}var n=P;P=t;try{return e()}finally{P=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=j,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=P;P=e;try{return t()}finally{P=n}},t.unstable_scheduleCallback=function(e,o,i){var l=t.unstable_now();switch("object"==typeof i&&null!==i?i="number"==typeof(i=i.delay)&&0<i?l+i:l:i=l,e){case 1:var s=-1;break;case 2:s=250;break;case 5:s=1073741823;break;case 4:s=1e4;break;default:s=5e3}return e={id:L++,callback:o,priorityLevel:e,startTime:i,expirationTime:s=i+s,sortIndex:-1},i>l?(e.sortIndex=i,E(T,e),null===S(C)&&e===S(T)&&(O?a():O=!0,r(I,i-l))):(e.sortIndex=s,E(C,e),N||R||(N=!0,n(M))),e},t.unstable_wrapCallback=function(e){var t=P;return function(){var n=P;P=t;try{return e.apply(this,arguments)}finally{P=n}}}},4616:(e,t,n)=>{"use strict";e.exports=n(6475)},8665:e=>{e.exports=function(e,t,n,r){var a=n?n.call(r,e,t):void 0;if(void 0!==a)return!!a;if(e===t)return!0;if("object"!=typeof e||!e||"object"!=typeof t||!t)return!1;var o=Object.keys(e),i=Object.keys(t);if(o.length!==i.length)return!1;for(var l=Object.prototype.hasOwnProperty.bind(t),s=0;s<o.length;s++){var c=o[s];if(!l(c))return!1;var u=e[c],d=t[c];if(!1===(a=n?n.call(r,u,d,c):void 0)||void 0===a&&u!==d)return!1}return!0}},1898:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var r="Invariant failed";function a(e,t){if(!e)throw new Error(r)}},6809:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={title:"LiveViewJS",tagline:"Simple yet powerful framework for LiveViews in NodeJS and Deno",url:"https://www.liveviewjs.com",baseUrl:"/",onBrokenLinks:"throw",onBrokenMarkdownLinks:"warn",favicon:"img/favicon.ico",trailingSlash:!1,organizationName:"floodfx",projectName:"liveviewjs",i18n:{defaultLocale:"en",locales:["en"],path:"i18n",localeConfigs:{}},presets:[["classic",{docs:{sidebarPath:"/Users/nicholasduffy/code/liveviewjs/apps/liveviewjs.com/sidebars.js"},blog:{showReadingTime:!0},theme:{customCss:"/Users/nicholasduffy/code/liveviewjs/apps/liveviewjs.com/src/css/custom.css"},gtag:{trackingID:"G-WXB1PQ0VMD",anonymizeIP:!0}}]],themeConfig:{navbar:{title:"LiveViewJS",logo:{alt:"LiveViewJS Logo",src:"img/logo.svg"},items:[{type:"doc",docId:"overview/introduction",position:"left",label:"Documentation"},{href:"https://github.com/floodfx/liveviewjs",label:"GitHub",position:"right"}],hideOnScroll:!1},footer:{style:"dark",links:[{title:"Docs",items:[{label:"Intro",to:"/docs/overview/introduction"}]},{title:"Community",items:[{label:"Stack Overflow",href:"https://stackoverflow.com/questions/tagged/liveviewjs"},{label:"Twitter",href:"https://twitter.com/liveviewjs"}]},{title:"More",items:[{label:"GitHub",href:"https://github.com/floodfx/liveviewjs"}]}],copyright:"Copyright \xa9 2023 Donnie Flood"},prism:{theme:{plain:{color:"#393A34",backgroundColor:"#f6f8fa"},styles:[{types:["comment","prolog","doctype","cdata"],style:{color:"#999988",fontStyle:"italic"}},{types:["namespace"],style:{opacity:.7}},{types:["string","attr-value"],style:{color:"#e3116c"}},{types:["punctuation","operator"],style:{color:"#393A34"}},{types:["entity","url","symbol","number","boolean","variable","constant","property","regex","inserted"],style:{color:"#36acaa"}},{types:["atrule","keyword","attr-name","selector"],style:{color:"#00a4db"}},{types:["function","deleted","tag"],style:{color:"#d73a49"}},{types:["function-variable"],style:{color:"#6f42c1"}},{types:["tag","selector","keyword"],style:{color:"#00009f"}}]},darkTheme:{plain:{color:"#F8F8F2",backgroundColor:"#282A36"},styles:[{types:["prolog","constant","builtin"],style:{color:"rgb(189, 147, 249)"}},{types:["inserted","function"],style:{color:"rgb(80, 250, 123)"}},{types:["deleted"],style:{color:"rgb(255, 85, 85)"}},{types:["changed"],style:{color:"rgb(255, 184, 108)"}},{types:["punctuation","symbol"],style:{color:"rgb(248, 248, 242)"}},{types:["string","char","tag","selector"],style:{color:"rgb(255, 121, 198)"}},{types:["keyword","variable"],style:{color:"rgb(189, 147, 249)",fontStyle:"italic"}},{types:["comment"],style:{color:"rgb(98, 114, 164)"}},{types:["attr-name"],style:{color:"rgb(241, 250, 140)"}}]},additionalLanguages:[],magicComments:[{className:"theme-code-block-highlighted-line",line:"highlight-next-line",block:{start:"highlight-start",end:"highlight-end"}}]},colorMode:{defaultMode:"light",disableSwitch:!1,respectPrefersColorScheme:!1},docs:{versionPersistence:"localStorage",sidebar:{hideable:!1,autoCollapseCategories:!1}},metadata:[],tableOfContents:{minHeadingLevel:2,maxHeadingLevel:3}},plugins:[null],baseUrlIssueBanner:!0,onDuplicateRoutes:"warn",staticDirectories:["static"],customFields:{},themes:[],scripts:[],stylesheets:[],clientModules:[],titleDelimiter:"|",noIndex:!1}},7896:(e,t,n)=>{"use strict";function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},r.apply(this,arguments)}n.d(t,{Z:()=>r})},2222:(e,t,n)=>{"use strict";function r(e,t){return r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},r(e,t)}function a(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,r(e,t)}n.d(t,{Z:()=>a})},1461:(e,t,n)=>{"use strict";function r(e,t){if(null==e)return{};var n,r,a={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(a[n]=e[n]);return a}n.d(t,{Z:()=>r})},7529:e=>{"use strict";e.exports={}},6887:e=>{"use strict";e.exports=JSON.parse('{"/blog-1d5":{"__comp":"a6aa9e1f","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","items":[{"content":"7661071f"},{"content":"f4f34a3a"},{"content":"8717b14a"},{"content":"925b3f96"}],"metadata":"b2b675dd"},"/blog/archive-809":{"__comp":"9e4087bc","__context":{"plugin":"bac9275f"},"archive":"b2f554cd"},"/blog/first-blog-post-72f":{"__comp":"ccc49370","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","content":"e273c56f"},"/blog/long-blog-post-52a":{"__comp":"ccc49370","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","content":"73664a40"},"/blog/mdx-blog-post-bf8":{"__comp":"ccc49370","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","content":"59362658"},"/blog/tags-8de":{"__comp":"01a85c17","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","tags":"a7023ddc"},"/blog/tags/docusaurus-138":{"__comp":"6875c492","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","items":[{"content":"7661071f"},{"content":"f4f34a3a"},{"content":"8717b14a"},{"content":"925b3f96"}],"tag":"a80da1cf","listMetadata":"608ae6a4"},"/blog/tags/facebook-2d9":{"__comp":"6875c492","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","items":[{"content":"7661071f"}],"tag":"031793e1","listMetadata":"096bfee4"},"/blog/tags/hello-193":{"__comp":"6875c492","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","items":[{"content":"7661071f"},{"content":"8717b14a"}],"tag":"30a24c52","listMetadata":"66406991"},"/blog/tags/hola-e44":{"__comp":"6875c492","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","items":[{"content":"925b3f96"}],"tag":"e16015ca","listMetadata":"4c9e35b1"},"/blog/welcome-905":{"__comp":"ccc49370","__context":{"plugin":"bac9275f"},"sidebar":"814f3328","content":"d9f32620"},"/docs-652":{"__comp":"1be78505","__context":{"plugin":"ebc9356c"},"versionMetadata":"935f2afb"},"/docs/anatomy-of-a-liveview/handle-event-details-c2a":{"__comp":"17896441","content":"15989f81"},"/docs/anatomy-of-a-liveview/handle-info-326":{"__comp":"17896441","content":"06e5e56e"},"/docs/anatomy-of-a-liveview/handle-info-background-task-645":{"__comp":"17896441","content":"284ba78b"},"/docs/anatomy-of-a-liveview/handle-info-pub-sub-868":{"__comp":"17896441","content":"218c334f"},"/docs/anatomy-of-a-liveview/handle-info-user-initiated-14f":{"__comp":"17896441","content":"26de17c9"},"/docs/anatomy-of-a-liveview/handle-params-a7c":{"__comp":"17896441","content":"be227171"},"/docs/anatomy-of-a-liveview/liveview-api-a61":{"__comp":"17896441","content":"e53ecdb2"},"/docs/anatomy-of-a-liveview/mount-details-c2b":{"__comp":"17896441","content":"9bbe50cd"},"/docs/anatomy-of-a-liveview/render-details-892":{"__comp":"17896441","content":"00bb4f2e"},"/docs/category/anatomy-of-a-liveview-5f6":{"__comp":"14eb3368","categoryGeneratedIndex":"42d1d491"},"/docs/category/client-side-javascript-a40":{"__comp":"14eb3368","categoryGeneratedIndex":"2043d2f8"},"/docs/category/forms--changesets-364":{"__comp":"14eb3368","categoryGeneratedIndex":"b747b5f5"},"/docs/category/js-commands-71b":{"__comp":"14eb3368","categoryGeneratedIndex":"fb8b1f32"},"/docs/category/lifecycle-of-a-liveview-060":{"__comp":"14eb3368","categoryGeneratedIndex":"9578e4ed"},"/docs/category/liveviewsocket-dff":{"__comp":"14eb3368","categoryGeneratedIndex":"c68b9697"},"/docs/category/miscellaneous-650":{"__comp":"14eb3368","categoryGeneratedIndex":"f98bd2c4"},"/docs/category/overview-a36":{"__comp":"14eb3368","categoryGeneratedIndex":"88b645ee"},"/docs/category/quick-starts-60d":{"__comp":"14eb3368","categoryGeneratedIndex":"f52ab4ca"},"/docs/category/real-time--multi-player-093":{"__comp":"14eb3368","categoryGeneratedIndex":"2c6e467f"},"/docs/category/uploading-files-e78":{"__comp":"14eb3368","categoryGeneratedIndex":"29143544"},"/docs/category/user-events-e0c":{"__comp":"14eb3368","categoryGeneratedIndex":"78c55e7e"},"/docs/category/webserver-integrations-481":{"__comp":"14eb3368","categoryGeneratedIndex":"2f79913e"},"/docs/client-javascript/client-hooks-3f6":{"__comp":"17896441","content":"eb3e9177"},"/docs/client-javascript/example-hook-548":{"__comp":"17896441","content":"76864381"},"/docs/client-javascript/overview-b84":{"__comp":"17896441","content":"42132283"},"/docs/file-upload/drag-and-drop-fda":{"__comp":"17896441","content":"b7a0085b"},"/docs/file-upload/image-preview-513":{"__comp":"17896441","content":"4f66b655"},"/docs/file-upload/overview-0e5":{"__comp":"17896441","content":"e96d4430"},"/docs/file-upload/upload-config-options-4cd":{"__comp":"17896441","content":"3081b21a"},"/docs/forms-and-changesets/changesets-178":{"__comp":"17896441","content":"355cffd0"},"/docs/forms-and-changesets/overview-20b":{"__comp":"17896441","content":"c1a16298"},"/docs/forms-and-changesets/use-with-forms-958":{"__comp":"17896441","content":"66ff5741"},"/docs/js-commands/add-remove-class-59b":{"__comp":"17896441","content":"bc63dc68"},"/docs/js-commands/dispatch-cmd-ec4":{"__comp":"17896441","content":"4ec89f2d"},"/docs/js-commands/overview-5da":{"__comp":"17896441","content":"6c9f2a26"},"/docs/js-commands/push-cmd-12f":{"__comp":"17896441","content":"7834bba6"},"/docs/js-commands/set-remove-attr-33c":{"__comp":"17896441","content":"d6e2e8d0"},"/docs/js-commands/show-hide-toggle-el-3d3":{"__comp":"17896441","content":"dddb404b"},"/docs/js-commands/transition-cmd-c60":{"__comp":"17896441","content":"0832cd5e"},"/docs/lifecycle-of-a-liveview/intro-263":{"__comp":"17896441","content":"630c0ad8"},"/docs/liveview-socket/liveviewsocket-api-43e":{"__comp":"17896441","content":"7d980dc2"},"/docs/liveview-socket/liveviewsocket-api-context-003":{"__comp":"17896441","content":"0e8a1ffd"},"/docs/liveview-socket/liveviewsocket-api-infos-ece":{"__comp":"17896441","content":"9b689780"},"/docs/liveview-socket/liveviewsocket-api-misc-fda":{"__comp":"17896441","content":"05a1a0e3"},"/docs/liveview-socket/liveviewsocket-api-push-369":{"__comp":"17896441","content":"f4424813"},"/docs/liveview-socket/liveviewsocket-api-uploads-a2d":{"__comp":"17896441","content":"ed398bd8"},"/docs/liveview-socket/raw-liveviewsocket-api-32f":{"__comp":"17896441","content":"a7e77012"},"/docs/misc/debugging-wire-fcc":{"__comp":"17896441","content":"12d121b1"},"/docs/misc/flash-aa2":{"__comp":"17896441","content":"0bd32f56"},"/docs/misc/livecomponents-2bd":{"__comp":"17896441","content":"f08e994e"},"/docs/misc/root-and-page-renderers-ead":{"__comp":"17896441","content":"3fc56706"},"/docs/misc/security-topics-2cb":{"__comp":"17896441","content":"a49e487e"},"/docs/misc/statics-and-dynamics-5bd":{"__comp":"17896441","content":"05efe52d"},"/docs/overview/feedback-ccc":{"__comp":"17896441","content":"88861ccc"},"/docs/overview/gratitude-384":{"__comp":"17896441","content":"11e44ae3"},"/docs/overview/introduction-7ac":{"__comp":"17896441","content":"ae0c09be"},"/docs/overview/paradigm-a3a":{"__comp":"17896441","content":"d8b15fc0"},"/docs/overview/runtimes-1ce":{"__comp":"17896441","content":"1fdf7751"},"/docs/quick-starts/deno-build-first-liveview-2c4":{"__comp":"17896441","content":"9a32815e"},"/docs/quick-starts/deno-run-examples-6ba":{"__comp":"17896441","content":"5f30c8bb"},"/docs/quick-starts/get-liveviewjs-repo-9bf":{"__comp":"17896441","content":"3de70efe"},"/docs/quick-starts/nodejs-build-first-liveview-d37":{"__comp":"17896441","content":"0ad92f1a"},"/docs/quick-starts/nodejs-run-examples-acf":{"__comp":"17896441","content":"33cd0f01"},"/docs/real-time-multi-player-pub-sub/example-pub-sub-839":{"__comp":"17896441","content":"d5bbcc50"},"/docs/real-time-multi-player-pub-sub/overview-00e":{"__comp":"17896441","content":"6bf4f685"},"/docs/user-events-slash-bindings/additional-bindings-4e4":{"__comp":"17896441","content":"ccf3ea07"},"/docs/user-events-slash-bindings/bindings-table-359":{"__comp":"17896441","content":"d4048a91"},"/docs/user-events-slash-bindings/overview-868":{"__comp":"17896441","content":"04151870"},"/docs/user-events-slash-bindings/rate-limiting-bindings-054":{"__comp":"17896441","content":"8908bd66"},"/docs/webserver-integration/liveview-server-adaptor-7f6":{"__comp":"17896441","content":"fe64d624"},"/docs/webserver-integration/overview-e05":{"__comp":"17896441","content":"9c3a86b6"},"/docs/webserver-integration/support-webserver-x-587":{"__comp":"17896441","content":"d63d7ef8"},"/-d5f":{"__comp":"1df93b7f","__context":{"plugin":"64a3b801"},"config":"5e9f5e1a"}}')}},e=>{e.O(0,[532],(()=>{return t=6445,e(e.s=t);var t}));e.O()}]); \ No newline at end of file diff --git a/docs/assets/js/main.bb008988.js.LICENSE.txt b/docs/assets/js/main.bb008988.js.LICENSE.txt deleted file mode 100644 index 6e08db29..00000000 --- a/docs/assets/js/main.bb008988.js.LICENSE.txt +++ /dev/null @@ -1,53 +0,0 @@ -/* -object-assign -(c) Sindre Sorhus -@license MIT -*/ - -/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress - * @license MIT */ - -/** - * Prism: Lightweight, robust, elegant syntax highlighting - * - * @license MIT <https://opensource.org/licenses/MIT> - * @author Lea Verou <https://lea.verou.me> - * @namespace - * @public - */ - -/** @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. - */ - -/** @license React v16.13.1 - * react-is.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. - */ - -/** @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. - */ - -/** @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. - */ diff --git a/docs/assets/js/runtime~main.bac7a942.js b/docs/assets/js/runtime~main.bac7a942.js deleted file mode 100644 index 64bfd013..00000000 --- a/docs/assets/js/runtime~main.bac7a942.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var e,f,a,b,d,c={},t={};function r(e){var f=t[e];if(void 0!==f)return f.exports;var a=t[e]={id:e,loaded:!1,exports:{}};return c[e].call(a.exports,a,a.exports,r),a.loaded=!0,a.exports}r.m=c,r.c=t,e=[],r.O=(f,a,b,d)=>{if(!a){var c=1/0;for(i=0;i<e.length;i++){a=e[i][0],b=e[i][1],d=e[i][2];for(var t=!0,o=0;o<a.length;o++)(!1&d||c>=d)&&Object.keys(r.O).every((e=>r.O[e](a[o])))?a.splice(o--,1):(t=!1,d<c&&(c=d));if(t){e.splice(i--,1);var n=b();void 0!==n&&(f=n)}}return f}d=d||0;for(var i=e.length;i>0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[a,b,d]},r.n=e=>{var f=e&&e.__esModule?()=>e.default:()=>e;return r.d(f,{a:f}),f},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,b){if(1&b&&(e=this(e)),8&b)return e;if("object"==typeof e&&e){if(4&b&&e.__esModule)return e;if(16&b&&"function"==typeof e.then)return e}var d=Object.create(null);r.r(d);var c={};f=f||[null,a({}),a([]),a(a)];for(var t=2&b&&e;"object"==typeof t&&!~f.indexOf(t);t=a(t))Object.getOwnPropertyNames(t).forEach((f=>c[f]=()=>e[f]));return c.default=()=>e,r.d(d,c),d},r.d=(e,f)=>{for(var a in f)r.o(f,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:f[a]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((f,a)=>(r.f[a](e,f),f)),[])),r.u=e=>"assets/js/"+({53:"935f2afb",110:"66406991",333:"f08e994e",450:"a7e77012",453:"30a24c52",458:"33cd0f01",533:"b2b675dd",610:"15989f81",645:"88861ccc",653:"eb3e9177",728:"2f79913e",914:"c68b9697",948:"8717b14a",1026:"05efe52d",1037:"a49e487e",1087:"d8b15fc0",1247:"3de70efe",1440:"9c3a86b6",1477:"b2f554cd",1523:"d4048a91",1544:"284ba78b",1633:"031793e1",1713:"a7023ddc",1894:"64a3b801",1914:"d9f32620",2267:"59362658",2294:"6bf4f685",2362:"e273c56f",2493:"76864381",2535:"814f3328",2672:"f98bd2c4",3089:"a6aa9e1f",3205:"a80da1cf",3219:"42d1d491",3237:"1df93b7f",3304:"0e8a1ffd",3369:"42132283",3380:"355cffd0",3476:"26de17c9",3514:"73664a40",3608:"9e4087bc",3776:"f52ab4ca",3919:"4f66b655",4013:"01a85c17",4085:"3fc56706",4161:"bac9275f",4221:"0ad92f1a",4379:"e96d4430",4819:"4ec89f2d",4919:"fe64d624",5024:"ae0c09be",5071:"6c9f2a26",5149:"fb8b1f32",5405:"2043d2f8",5539:"66ff5741",5548:"9a32815e",5723:"9b689780",5727:"06e5e56e",5742:"0832cd5e",5967:"7834bba6",6011:"dddb404b",6103:"ccc49370",6119:"c1a16298",6242:"d6e2e8d0",6250:"29143544",6258:"b7a0085b",6300:"be227171",6304:"2c6e467f",6527:"8908bd66",6561:"78c55e7e",6628:"f4424813",6739:"11e44ae3",6847:"04151870",6938:"608ae6a4",7178:"096bfee4",7203:"218c334f",7599:"b747b5f5",7800:"1fdf7751",7875:"ccf3ea07",7911:"630c0ad8",7918:"17896441",8030:"0bd32f56",8168:"88b645ee",8259:"5f30c8bb",8379:"05a1a0e3",8610:"6875c492",8636:"f4f34a3a",8685:"d63d7ef8",8686:"9578e4ed",9003:"925b3f96",9035:"4c9e35b1",9080:"9bbe50cd",9119:"bc63dc68",9133:"12d121b1",9376:"7d980dc2",9484:"e53ecdb2",9514:"1be78505",9563:"00bb4f2e",9642:"7661071f",9700:"e16015ca",9773:"ed398bd8",9817:"14eb3368",9843:"d5bbcc50",9944:"3081b21a",9991:"ebc9356c"}[e]||e)+"."+{53:"42240f25",110:"6557cce3",333:"f1826f7a",450:"33ce6ba3",453:"1683ba64",458:"e15f00ce",533:"aad3112b",610:"aaa42acf",645:"20e78eba",653:"8cc4ebe8",728:"df91907d",914:"0d9df512",948:"57b843ef",1026:"09d979ec",1037:"d097d48c",1087:"f830a4a0",1247:"6513b0c9",1440:"61f78c9a",1477:"15a03ba7",1523:"0f420394",1544:"71677939",1633:"42487eea",1713:"fbeef8ab",1894:"bc70b5ca",1914:"2fdf6156",2006:"75ebd0c6",2267:"4deb710c",2294:"9876148d",2362:"1d51bf07",2493:"a33c114b",2535:"63c77d93",2672:"65b4ee08",3089:"b9c01e8d",3205:"3f59ef08",3219:"cf07e823",3237:"019f5c69",3304:"3bb752e2",3369:"343e6bdb",3380:"93571283",3476:"0e3042a8",3514:"4ae7ea32",3608:"7cfaacea",3776:"2a9123be",3919:"9614f120",4013:"41cb6eb6",4085:"15e86c2f",4161:"48905d42",4221:"c6883c73",4379:"d98ef097",4819:"d296e41b",4919:"2d20b345",5024:"4134591f",5071:"46cf69f9",5149:"6f166156",5405:"82865eed",5539:"c2237126",5548:"dcb2119a",5674:"2cada8ea",5723:"e50ad980",5727:"5ea46c99",5742:"baac617e",5967:"8d670bf6",6011:"abf300d4",6103:"016b92cf",6119:"5abdd7fc",6242:"96c3ccce",6250:"a57f485d",6258:"997180ee",6300:"2d38958c",6304:"65dfc83b",6468:"7f78e9f0",6527:"557693cf",6561:"59d69983",6628:"c1da1dc0",6739:"c955dcae",6847:"ecaffa18",6938:"5be44012",7178:"9982f285",7203:"b024827c",7599:"70f727bd",7800:"913e1f07",7875:"ed328d0a",7911:"bc45891b",7918:"0c65f063",8030:"9cbc10ae",8168:"bbd3a54f",8259:"5f093f7f",8379:"d2b49d64",8610:"5c47c8f7",8636:"1d3ce6eb",8685:"33b1b0a0",8686:"8fdbb7d4",9003:"2a134060",9035:"7fb6c67c",9080:"dcc99ddb",9119:"3f7e97f5",9133:"9e468a56",9376:"4c6a6448",9484:"921f2dab",9514:"b84d86db",9563:"d6da3795",9642:"5843a255",9700:"383724d8",9773:"068419c2",9817:"1ae9325c",9843:"33ddf498",9944:"ce19d10e",9991:"c0f8c26c"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),b={},d="liveviewjs-com:",r.l=(e,f,a,c)=>{if(b[e])b[e].push(f);else{var t,o;if(void 0!==a)for(var n=document.getElementsByTagName("script"),i=0;i<n.length;i++){var l=n[i];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==d+a){t=l;break}}t||(o=!0,(t=document.createElement("script")).charset="utf-8",t.timeout=120,r.nc&&t.setAttribute("nonce",r.nc),t.setAttribute("data-webpack",d+a),t.src=e),b[e]=[f];var u=(f,a)=>{t.onerror=t.onload=null,clearTimeout(s);var d=b[e];if(delete b[e],t.parentNode&&t.parentNode.removeChild(t),d&&d.forEach((e=>e(a))),f)return f(a)},s=setTimeout(u.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=u.bind(null,t.onerror),t.onload=u.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918",29143544:"6250",42132283:"3369",59362658:"2267",66406991:"110",76864381:"2493","935f2afb":"53",f08e994e:"333",a7e77012:"450","30a24c52":"453","33cd0f01":"458",b2b675dd:"533","15989f81":"610","88861ccc":"645",eb3e9177:"653","2f79913e":"728",c68b9697:"914","8717b14a":"948","05efe52d":"1026",a49e487e:"1037",d8b15fc0:"1087","3de70efe":"1247","9c3a86b6":"1440",b2f554cd:"1477",d4048a91:"1523","284ba78b":"1544","031793e1":"1633",a7023ddc:"1713","64a3b801":"1894",d9f32620:"1914","6bf4f685":"2294",e273c56f:"2362","814f3328":"2535",f98bd2c4:"2672",a6aa9e1f:"3089",a80da1cf:"3205","42d1d491":"3219","1df93b7f":"3237","0e8a1ffd":"3304","355cffd0":"3380","26de17c9":"3476","73664a40":"3514","9e4087bc":"3608",f52ab4ca:"3776","4f66b655":"3919","01a85c17":"4013","3fc56706":"4085",bac9275f:"4161","0ad92f1a":"4221",e96d4430:"4379","4ec89f2d":"4819",fe64d624:"4919",ae0c09be:"5024","6c9f2a26":"5071",fb8b1f32:"5149","2043d2f8":"5405","66ff5741":"5539","9a32815e":"5548","9b689780":"5723","06e5e56e":"5727","0832cd5e":"5742","7834bba6":"5967",dddb404b:"6011",ccc49370:"6103",c1a16298:"6119",d6e2e8d0:"6242",b7a0085b:"6258",be227171:"6300","2c6e467f":"6304","8908bd66":"6527","78c55e7e":"6561",f4424813:"6628","11e44ae3":"6739","04151870":"6847","608ae6a4":"6938","096bfee4":"7178","218c334f":"7203",b747b5f5:"7599","1fdf7751":"7800",ccf3ea07:"7875","630c0ad8":"7911","0bd32f56":"8030","88b645ee":"8168","5f30c8bb":"8259","05a1a0e3":"8379","6875c492":"8610",f4f34a3a:"8636",d63d7ef8:"8685","9578e4ed":"8686","925b3f96":"9003","4c9e35b1":"9035","9bbe50cd":"9080",bc63dc68:"9119","12d121b1":"9133","7d980dc2":"9376",e53ecdb2:"9484","1be78505":"9514","00bb4f2e":"9563","7661071f":"9642",e16015ca:"9700",ed398bd8:"9773","14eb3368":"9817",d5bbcc50:"9843","3081b21a":"9944",ebc9356c:"9991"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(f,a)=>{var b=r.o(e,f)?e[f]:void 0;if(0!==b)if(b)a.push(b[2]);else if(/^(1303|532)$/.test(f))e[f]=0;else{var d=new Promise(((a,d)=>b=e[f]=[a,d]));a.push(b[2]=d);var c=r.p+r.u(f),t=new Error;r.l(c,(a=>{if(r.o(e,f)&&(0!==(b=e[f])&&(e[f]=void 0),b)){var d=a&&("load"===a.type?"missing":a.type),c=a&&a.target&&a.target.src;t.message="Loading chunk "+f+" failed.\n("+d+": "+c+")",t.name="ChunkLoadError",t.type=d,t.request=c,b[1](t)}}),"chunk-"+f,f)}},r.O.j=f=>0===e[f];var f=(f,a)=>{var b,d,c=a[0],t=a[1],o=a[2],n=0;if(c.some((f=>0!==e[f]))){for(b in t)r.o(t,b)&&(r.m[b]=t[b]);if(o)var i=o(r)}for(f&&f(a);n<c.length;n++)d=c[n],r.o(e,d)&&e[d]&&e[d][0](),e[d]=0;return r.O(i)},a=self.webpackChunkliveviewjs_com=self.webpackChunkliveviewjs_com||[];a.forEach(f.bind(null,0)),a.push=f.bind(null,a.push.bind(a))})()})(); \ No newline at end of file diff --git a/docs/blog.html b/docs/blog.html deleted file mode 100644 index 8670f779..00000000 --- a/docs/blog.html +++ /dev/null @@ -1,25 +0,0 @@ -<!doctype html> -<html lang="en" dir="ltr" class="blog-wrapper blog-list-page plugin-blog plugin-id-default"> -<head> -<meta charset="UTF-8"> -<meta name="viewport" content="width=device-width,initial-scale=1"> -<meta name="generator" content="Docusaurus v2.0.1"> -<link rel="alternate" type="application/rss+xml" href="/blog/rss.xml" title="LiveViewJS RSS Feed"> -<link rel="alternate" type="application/atom+xml" href="/blog/atom.xml" title="LiveViewJS Atom Feed"> - -<link rel="preconnect" href="https://www.google-analytics.com"> -<link rel="preconnect" href="https://www.googletagmanager.com"> -<script async src="https://www.googletagmanager.com/gtag/js?id=G-WXB1PQ0VMD"></script> -<script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-WXB1PQ0VMD",{anonymize_ip:!0})</script><title data-rh="true">Blog | LiveViewJS - - - - -
-

· One min read
Donnie Flood

Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· One min read
Donnie Flood

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- - - - \ No newline at end of file diff --git a/docs/blog/archive.html b/docs/blog/archive.html deleted file mode 100644 index 649f6c0c..00000000 --- a/docs/blog/archive.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Archive | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/blog/atom.xml b/docs/blog/atom.xml deleted file mode 100644 index 9695950e..00000000 --- a/docs/blog/atom.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - https://www.liveviewjs.com/blog - LiveViewJS Blog - 2021-08-26T00:00:00.000Z - https://github.com/jpmonette/feed - - LiveViewJS Blog - https://www.liveviewjs.com/img/favicon.ico - - <![CDATA[Welcome]]> - welcome - - 2021-08-26T00:00:00.000Z - - Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

]]>
- - Donnie Flood - https://github.com/floodfx - - - - -
- - <![CDATA[MDX Blog Post]]> - mdx-blog-post - - 2021-08-01T00:00:00.000Z - - Blog posts support Docusaurus Markdown features, such as MDX.

tip

Use the power of React to create interactive blog posts.

<button onClick={() => alert("button clicked!")}>Click me!</button>
]]>
- - Donnie Flood - https://github.com/floodfx - - -
- - <![CDATA[Long Blog Post]]> - long-blog-post - - 2019-05-29T00:00:00.000Z - - This is the summary of a very long blog post,

Use a <!-- truncate --> comment to limit blog post size in the list view.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

]]>
- - Donnie Flood - https://github.com/floodfx - - - -
- - <![CDATA[First Blog Post]]> - first-blog-post - - 2019-05-28T00:00:00.000Z - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

]]>
- - Donnie Flood - https://github.com/floodfx - - - -
-
\ No newline at end of file diff --git a/docs/blog/first-blog-post.html b/docs/blog/first-blog-post.html deleted file mode 100644 index 044302de..00000000 --- a/docs/blog/first-blog-post.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -First Blog Post | LiveViewJS - - - - -
-

First Blog Post

· One min read
Donnie Flood

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- - - - \ No newline at end of file diff --git a/docs/blog/long-blog-post.html b/docs/blog/long-blog-post.html deleted file mode 100644 index 4808f853..00000000 --- a/docs/blog/long-blog-post.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - -Long Blog Post | LiveViewJS - - - - -
-

Long Blog Post

· 3 min read
Donnie Flood

This is the summary of a very long blog post,

Use a <!-- truncate --> comment to limit blog post size in the list view.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- - - - \ No newline at end of file diff --git a/docs/blog/mdx-blog-post.html b/docs/blog/mdx-blog-post.html deleted file mode 100644 index bd23ef5e..00000000 --- a/docs/blog/mdx-blog-post.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -MDX Blog Post | LiveViewJS - - - - -
-

MDX Blog Post

· One min read
Donnie Flood

Blog posts support Docusaurus Markdown features, such as MDX.

tip

Use the power of React to create interactive blog posts.

<button onClick={() => alert("button clicked!")}>Click me!</button>
- - - - \ No newline at end of file diff --git a/docs/blog/rss.xml b/docs/blog/rss.xml deleted file mode 100644 index 8d718d4c..00000000 --- a/docs/blog/rss.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - LiveViewJS Blog - https://www.liveviewjs.com/blog - LiveViewJS Blog - Thu, 26 Aug 2021 00:00:00 GMT - https://validator.w3.org/feed/docs/rss2.html - https://github.com/jpmonette/feed - en - - <![CDATA[Welcome]]> - https://www.liveviewjs.com/blog/welcome - welcome - Thu, 26 Aug 2021 00:00:00 GMT - - Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

]]>
- facebook - hello - docusaurus -
- - <![CDATA[MDX Blog Post]]> - https://www.liveviewjs.com/blog/mdx-blog-post - mdx-blog-post - Sun, 01 Aug 2021 00:00:00 GMT - - Blog posts support Docusaurus Markdown features, such as MDX.

tip

Use the power of React to create interactive blog posts.

<button onClick={() => alert("button clicked!")}>Click me!</button>
]]>
- docusaurus -
- - <![CDATA[Long Blog Post]]> - https://www.liveviewjs.com/blog/long-blog-post - long-blog-post - Wed, 29 May 2019 00:00:00 GMT - - This is the summary of a very long blog post,

Use a <!-- truncate --> comment to limit blog post size in the list view.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

]]>
- hello - docusaurus -
- - <![CDATA[First Blog Post]]> - https://www.liveviewjs.com/blog/first-blog-post - first-blog-post - Tue, 28 May 2019 00:00:00 GMT - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

]]>
- hola - docusaurus -
-
-
\ No newline at end of file diff --git a/docs/blog/tags.html b/docs/blog/tags.html deleted file mode 100644 index 04eb11d3..00000000 --- a/docs/blog/tags.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Tags | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/blog/tags/docusaurus.html b/docs/blog/tags/docusaurus.html deleted file mode 100644 index a6da5615..00000000 --- a/docs/blog/tags/docusaurus.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - -4 posts tagged with "docusaurus" | LiveViewJS - - - - -
-

4 posts tagged with "docusaurus"

View All Tags

· One min read
Donnie Flood

Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

· One min read
Donnie Flood

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- - - - \ No newline at end of file diff --git a/docs/blog/tags/facebook.html b/docs/blog/tags/facebook.html deleted file mode 100644 index 1c10661b..00000000 --- a/docs/blog/tags/facebook.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -One post tagged with "facebook" | LiveViewJS - - - - -
-

One post tagged with "facebook"

View All Tags

· One min read
Donnie Flood

Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- - - - \ No newline at end of file diff --git a/docs/blog/tags/hello.html b/docs/blog/tags/hello.html deleted file mode 100644 index e9b686cb..00000000 --- a/docs/blog/tags/hello.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -2 posts tagged with "hello" | LiveViewJS - - - - -
-

2 posts tagged with "hello"

View All Tags

· One min read
Donnie Flood

Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- - - - \ No newline at end of file diff --git a/docs/blog/tags/hola.html b/docs/blog/tags/hola.html deleted file mode 100644 index 4f9573c7..00000000 --- a/docs/blog/tags/hola.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -One post tagged with "hola" | LiveViewJS - - - - -
-

One post tagged with "hola"

View All Tags

· One min read
Donnie Flood

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum -tempor eros aliquam consequat. Lorem ipsum dolor sit amet

- - - - \ No newline at end of file diff --git a/docs/blog/welcome.html b/docs/blog/welcome.html deleted file mode 100644 index 2893a9fd..00000000 --- a/docs/blog/welcome.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -Welcome | LiveViewJS - - - - -
-

Welcome

· One min read
Donnie Flood

Docusaurus blogging features are powered by the -blog plugin.

Simply add Markdown files (or folders) to the blog directory.

Regular blog authors can be added to authors.yml.

The blog post date can be extracted from filenames, such as:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

A blog post folder can be convenient to co-locate blog post images:

Docusaurus Plushie

The blog supports tags as well!

And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.

- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/handle-event-details.html b/docs/docs/anatomy-of-a-liveview/handle-event-details.html deleted file mode 100644 index b26b2452..00000000 --- a/docs/docs/anatomy-of-a-liveview/handle-event-details.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - -LiveView API - `handleEvent` | LiveViewJS - - - - -
-

LiveView API - handleEvent

handleEvent is called automatically by the LiveViewJS framework when a user action causes the browser to send -an event to the server (e.g., clicks, keyboard input, form updates, focus/blur, etc.). (More details on -User Events). The handleEvent function is responsible for updating the -context (i.e., state) of the LiveView based on the event.

handleEvent Signature

handleEvent(event: TEvents, socket: LiveViewSocket<TContext, TInfos>): void | Promise<void>;

The example handleEvent function below receives the event and the socket and updates the count in the socket's -context based on the event's type. In other words, it adds 1 when to count when it receives the increment event -and subtracts 1 when it receives the decrement event:

counterLiveView.ts
import { createLiveView, html } from "liveviewjs";
/**
* A basic counter that increments and decrements a number.
*/
export const counterLiveView = createLiveView<
{ count: number }, // Define LiveView Context / State
{ type: "increment" } | { type: "decrement" } // Define LiveView Events
>({
// Setup / initialize the LiveView Context (i.e., set count to 0)
mount: (socket) => {
socket.assign({ count: 0 });
},
// Handle incoming increment and decrement events from User input
handleEvent: (event, socket) => {
const { count } = socket.context;
switch (event.type) {
case "increment":
socket.assign({ count: count + 1 });
break;
case "decrement":
socket.assign({ count: count - 1 });
break;
}
},
// Renders the Counter View based on the current Context / State
render: (context) => {
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});
- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/handle-info-background-task.html b/docs/docs/anatomy-of-a-liveview/handle-info-background-task.html deleted file mode 100644 index 293d6d22..00000000 --- a/docs/docs/anatomy-of-a-liveview/handle-info-background-task.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -Background Task with `handleInfo` | LiveViewJS - - - - -
-

Background Task with handleInfo

A "live" dashboard that updates with the latest metrics periodically is another use case that shines with server events -handled asynchronously by handleInfo.

Example Live Dashboard LiveView

dashboardLiveView.ts
/**
* A dashboard that automatically refreshes every second
*/
export const dashboardLiveView = createLiveView<
// Define LiveView Context / State
{ newOrders: number; salesAmount: number; rating: number },
// Define LiveView External Events
{} // No external events
// Define LiveView Internal Events
{ type: "tick" }
>({
mount: (socket) => {
if (socket.connected) {
// only start repeating if the socket is connected (i.e., websocket is connected)
socket.repeat(() => {
// send the tick event to `handleInfo` every second
socket.sendInfo({ type: "tick" });
}, 1000);
}
socket.assign(nextRandomData());
},
// on tick, update data
handleInfo: (_, socket) => socket.assign(fetchLatestData()),
// render the dashboard
render: (context) => {
const { newOrders, salesAmount, rating } = context;
return html`
<h1>Sales Dashboard</h1>
<hr />
<span>🥡 New Orders</span>
<h2>${newOrders}</h2>
<hr />
<span>💰 Sales Amount</span>
<h2>${salesAmount}</h2>
<hr />
<span>🌟 Rating</spa>
<h2>${rating}</h2>
`;
},
});
note

The fetchLatestData method is not shown here because the implementation is not important. Just assume it -return the latest order, sales, and review data from a database, feed, API, etc.

How it works

  • The LiveView renders a dashboard that refreshes every second with the latest order, sales, and review data.
  • mount kicks off the repeat function that sends a tick event to handleInfo every second.
  • The handleInfo method then fetches the data asynchronously and updates the context with the latest data.
  • When the latest data is successfully fetched, the context is updated, which causes the render method to be called -again, pushing the latest data to the client.

handleInfo Use Cases

There are three main use cases for handleInfo:

  • Handling an asynchronous process initiated from a user event without blocking the UI
  • Handling an asynchronous process initiated from a background process
  • Handling a pub/sub message
- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/handle-info-pub-sub.html b/docs/docs/anatomy-of-a-liveview/handle-info-pub-sub.html deleted file mode 100644 index fccb7128..00000000 --- a/docs/docs/anatomy-of-a-liveview/handle-info-pub-sub.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - -Pub/Sub with `handleInfo` | LiveViewJS - - - - -
-

Pub/Sub with handleInfo

Pub/Sub is a common pattern for decoupling processes by allowing messages to be sent to a topic by one process and -received asynchronously by another. LiveViewJS (and Phoenix LiveView, for that matter) are built on top of Pub/Sub, -and Pub/Sub is what enables building the real-time, multi-player features with such ease (along with the LiveView -programming model). We will go into more detail on Pub/Sub in the Real-Time -Multi-Player docs.

Example Pub/Sub LiveView

We're going to extend our counter example to use Pub/Sub, which will make it a real-time, multi-player counter. Here is -the code with the Pub/Sub changes highlighted:

realtimeCounterLiveView.ts
import { createLiveView, html, SingleProcessPubSub } from "liveviewjs";

// An in-memory count simulating state outside of the LiveView
let count = 0;
// Use a single process pub/sub implementation (for simplicity)
const pubSub = new SingleProcessPubSub();

/**
* A basic counter that increments and decrements a number.
*/
export const rtCounterLiveView = createLiveView<
{ count: number }, // Define LiveView Context / State
{ type: "increment" } | { type: "decrement" }, // Define LiveView Events
{ type: "counter"; count: number } // Define LiveView Info messages
>({
mount: (socket) => {
// init state, set count to current count
socket.assign({ count });
// subscribe to counter events
socket.subscribe("counter");
},
handleEvent: (event, socket) => {
// handle increment and decrement events
const { count } = socket.context;
switch (event.type) {
case "increment":
// broadcast the new count
pubSub.broadcast("counter", { count: count + 1 });
break;
case "decrement":
// broadcast the new count
pubSub.broadcast("counter", { count: count - 1 });
break;
}
},
handleInfo: (info, socket) => {
// receive updates from pubsub and update the context
count = info.count;
socket.assign({ count });
},
render: async (context) => {
// render the view based on the state
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});
info

We're using a SingleProcessPubSub implementation for simplicity. In a real application, you would use a -RedisPubSub implementation in NodeJS or a BroadcastChannelPubSub implementation in for Deno. See the -Pub/Sub docs for more details.

How it works

  • This works just like the counter.ts example except we're using Pub/Sub to broadcast the new count to all connected -clients and subscribe to updates from other clients.
  • When a client clicks the increment or decrement button, we broadcast the new count to all connected clients using -pubSub.broadcast.
  • The LiveViewJS framework automatically routes messages from pubSub.broadcast to the handleInfo function for -any LiveView subscribed to the topic.
  • In this case, handleInfo receives the new count and updates the LiveView context which re-renders the view.

It's that easy!

In ~10 lines of code, we've built a real-time, multi-player counter! Sure, that isn't particularly useful, but it shows you how easy it is to create real-time, multi-player applications with very little code and effort.

- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/handle-info-user-initiated.html b/docs/docs/anatomy-of-a-liveview/handle-info-user-initiated.html deleted file mode 100644 index 1df8f401..00000000 --- a/docs/docs/anatomy-of-a-liveview/handle-info-user-initiated.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -User-Initiated Event with `handleInfo` | LiveViewJS - - - - -
-

User-Initiated Event with handleInfo

Search is a common use case where a user-initiated event might be handled by handleInfo.

Example Search LiveView

searchLiveView.ts
import { createLiveView, html } from "liveviewjs";
import {searchUsers} from "../services/searchUsers";
/**
* A basic search that searches for a user by name.
*/
export const searchLiveView = createLiveView<
// Define LiveView Context / State
{ search: string; results: string[], loading: boolean },
// Define LiveView Events
{ type: "search"; search: string }
// Define LiveView Infos
{ type: "doSearch"; search: string }
>({
// Setup / initialize the LiveView Context (i.e., set search to "" and results to [])
mount: (socket) => {
socket.assign({ search: "", results: [] });
},
// Handle incoming search events from User input
handleEvent: (event, socket) => {
const { search } = socket.context;
switch (event.type) {
case "search":
// set the search data and loading in the context
socket.assign({ search: event.search, loading: true });
// Send a doSearch info (event) to the `handleInfo` method
socket.sendInfo({ type: "doSearch", search: event.search });
break;
}
},
// Handle incoming info events from the server
handleInfo: (info, socket) => {
const { search } = socket.context;
switch (info.type) {
case "doSearch":
// Search for users and update the results in the context
const results = await searchUsers(info.search);
socket.assign({ results, loading: false });
break;
}
},
// Renders the Search View based on the current Context / State
render: (context) => {
const { search, results } = context;
return html`
<div>
<h1>Search for a user</h1>
<input
type="text"
placeholder="Search for a user"
value=${search}
phx-change="search"
/>
${renderResults(results, loading)}
</div>
`;
},
});

function renderResults(results: string[], loading: boolean) {
if (loading) {
return html`<div>Loading...</div>`;
}
if (results.length === 0) {
return html`<div>No results</div>`;
}
return html`
<ul>
${results.map((result) => html`<li>${result}</li>`)}
</ul>
`;
}

How it works

  • The LiveView renders a form that allows a user to search for a user by name. When the user submits the form, the -handleEvent method is called with the search event.
  • The handleEvent method then updates the context with the search text, sets loading to true, and sends a -doSearch info event to the handleInfo method.
  • The handleInfo method then performs the search asynchronously (i.e., it doesn't block rendering from the -handleEvent).
  • When the search is completed handleInfo and updates the results in the context and sets loading to false. -Updating the context causes the render method to be called again, which renders the search results.

handleInfo Use Cases

There are three main use cases for handleInfo:

  • Handling an asynchronous process initiated from a user event without blocking the UI
  • Handling an asynchronous process initiated from a background process
  • Handling a pub/sub message
- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/handle-info.html b/docs/docs/anatomy-of-a-liveview/handle-info.html deleted file mode 100644 index 4ad4fc42..00000000 --- a/docs/docs/anatomy-of-a-liveview/handle-info.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -LiveView API - `handleInfo` | LiveViewJS - - - - -
-

LiveView API - handleInfo

handleInfo is how server-side events (a.k.a Info) are handled. These server-side events are initiated by processes -that are happening on the server for example: database updates, background jobs, pub/sub messages, or some other -asynchronous process. Just like handleEvent and handleParams, handleInfo is automatically passed the info event -(i.e., server event) along with the socket and can use it to manipulate the context of the LiveView or otherwise -respond to the info messages it receives.

handleInfo Signature

handleInfo(info: TInfos, socket: LiveViewSocket<TContext, TInfos>): void | Promise<void>;

handleInfo Use Cases

There are three main use cases for handleInfo:

  • Handling an asynchronous process initiated from a user event without blocking the UI
  • Handling an asynchronous process initiated from a background process
  • Handling a pub/sub message
- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/handle-params.html b/docs/docs/anatomy-of-a-liveview/handle-params.html deleted file mode 100644 index 1ab8680e..00000000 --- a/docs/docs/anatomy-of-a-liveview/handle-params.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - -LiveView API - `handleParams` | LiveViewJS - - - - -
-

LiveView API - handleParams

Let's explore the handleParams method. Since the previous example (counterLiveView) did not use handleParams, -we'll define helloLiveView.ts and explore the handleParams method with it. As you can see below, helloLiveView.ts -defines mount, handleParams, and render.

helloLiveView.ts
export const helloLiveView = createLiveView({
mount: (socket) => {
socket.assign({ name: "World" });
},
handleParams(url, socket) {
const name = url.searchParams.get("name") || "World";
socket.assign({ name });
},
render: (context) => html`Hello ${context.name}!`,
});

In the case of helloLiveView, we are using handleParams to update the name in the context based on the name -query parameter in the URL. If there is no name query parameter, we default to World.

Example Renders

Let's say you have the helloLiveView routed to /hello. Visiting the following paths would result in the following -renders:

  • /hello - Hello World!
  • /hello?name=LiveViewJS - Hello LiveViewJS!
  • /hello?name=LiveViewJS&foo=bar - Hello LiveViewJS!
  • /hello?name=LiveViewJS#blah - Hello LiveViewJS!

handleParams Method

handleParams is automatically called by LiveViewJS on the initial load of a LiveView, as well as anytime the URL of -the LiveView changes. handleParams allows developers to access the full URL of the LiveView including the host, -path, hash, pathname, etc, and then update the context of the socket or otherwise respond to data in the URL.

note

Worth noting that the http server (e.g., express or oak) handles the routing of the browser to this LiveView. This -means that changes in the URL for handleParams are typically search parameters or hash changes. Changing the host -and/or path of a URL will typically mean the server routes you to a different LiveView (if one exists at that host and -path).

handleParams Signature

handleParams(url: URL, socket: LiveViewSocket<TContext, TInfos>): void | Promise<void>;
info

The URL passed to the handleParams method is the standard URL object, not a LiveViewJS specific URL -object. See the MDN URL documentation for more information.

- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/liveview-api.html b/docs/docs/anatomy-of-a-liveview/liveview-api.html deleted file mode 100644 index d573328e..00000000 --- a/docs/docs/anatomy-of-a-liveview/liveview-api.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - -LiveView API | LiveViewJS - - - - -
-

LiveView API

note

We are going to be using Typescript in our examples because LiveViewJS is very thoroughly typed, which -provides great type hints, autocompletion, etc. If the typescript syntax is confusing, just ignore it and focus on the -code.

LiveView API is Five Methods

The LiveViewJS API is extremely simple but very flexible. There are only five methods that make up the LiveView API: -mount, render, handleEvent, handleInfo, and handleParams. Technically, only render is required. The other -four methods (mount, handleEvent, handleInfo, handleParams) are optional but usually mount and at least one -other handle method is defined to enable a dynamic experience.

info

The smallest, valid LiveView only defines render like so:

const helloLiveView = createLiveView({
render: () => html`Hello World`,
});

While "valid" a LiveView like this is not very useful nor particularly exciting. Let's look at a more useful example.

Example LiveView Implementation

It's helpful to look at a simple LiveView example to see how the LiveView API works. Here is a simple LiveView that -renders a counter and has buttons to increment and decrement the counter:

counterLiveView.ts
import { createLiveView, html } from "liveviewjs";
/**
* A basic counter that increments and decrements a number.
*/
export const counterLiveView = createLiveView<
{ count: number }, // Define LiveView Context / State
{ type: "increment" } | { type: "decrement" } // Define LiveView Events
>({
// Setup / initialize the LiveView Context (i.e., set count to 0)
mount: (socket) => {
socket.assign({ count: 0 });
},
// Handle incoming increment and decrement events from User input
handleEvent: (event, socket) => {
const { count } = socket.context;
switch (event.type) {
case "increment":
socket.assign({ count: count + 1 });
break;
case "decrement":
socket.assign({ count: count - 1 });
break;
}
},
// Renders the Counter View based on the current Context / State
render: (context) => {
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});

Before we look at the five LiveView methods a very quick aside on the first line of the example above:

import { createLiveView, html } from "liveviewjs";

createLiveView helper

LiveViewJS provides various helpers to create and implement LiveViews. The createLiveView function is the canonical -way to define the various functions that make up a LiveView (mount, render etc) and supports typing the LiveView via -Typescript annotations TContext, TEvents, and TInfo.

html helper

The html function is a tagged template literal that allows you to write HTML with dynamic content in a very normal way -using javascript -template literals. The html tag -handles escaping content to prevent injection attacks but just as importantly, the html tag also -(transparently) creates the data structure necessary to efficiently calculate diffs between the current HTML and the new -HTML. This is what allows LiveViewJS to efficiently update the DOM with only the changes.

Let's take a closer look at mount, render, handleEvent, handleInfo, and handleParams methods in the next -sections.

- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/mount-details.html b/docs/docs/anatomy-of-a-liveview/mount-details.html deleted file mode 100644 index 285bd2df..00000000 --- a/docs/docs/anatomy-of-a-liveview/mount-details.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - -LiveView API - `mount` | LiveViewJS - - - - -
-

LiveView API - mount

mount is called by the LiveViewJS runtime when your LiveView is first mounted -(over HTTP and Websocket). mount is where you initialize the context (i.e. -state) of your LiveView (using socket.assign) and otherwise configure the LiveView. The -webserver integrations automatically make session data available via the -session which can be useful if you need to use data from the user's session. Don't worry about params for now. We'll -cover that later.

mount Signature

mount(
socket: LiveViewSocket<TContext, TInfos>,
session: Partial<SessionData>,
params: LiveViewMountParams
): void | Promise<void>;

As you can see in the counterLiveView.ts below, mount initializes the count to 0 (and doesn't use the session -or params):

counterLiveView.ts
import { createLiveView, html } from "liveviewjs";
/**
* A basic counter that increments and decrements a number.
*/
export const counterLiveView = createLiveView<
{ count: number }, // Define LiveView Context / State
{ type: "increment" } | { type: "decrement" } // Define LiveView Events
>({
// Setup / initialize the LiveView Context (i.e., set count to 0)
mount: (socket) => {
socket.assign({ count: 0 });
},
// Handle incoming increment and decrement events from User input
handleEvent: (event, socket) => {
const { count } = socket.context;
switch (event.type) {
case "increment":
socket.assign({ count: count + 1 });
break;
case "decrement":
socket.assign({ count: count - 1 });
break;
}
},
// Renders the Counter View based on the current Context / State
render: (context) => {
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});
info

The LiveViewSocket is passed into all methods except for render. LiveViewSocket is the swiss army knife of -LiveViewJS. We will cover its API in more detail shortly.

- - - - \ No newline at end of file diff --git a/docs/docs/anatomy-of-a-liveview/render-details.html b/docs/docs/anatomy-of-a-liveview/render-details.html deleted file mode 100644 index c8ed03c8..00000000 --- a/docs/docs/anatomy-of-a-liveview/render-details.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - -LiveView API - `render` | LiveViewJS - - - - -
-

LiveView API - render

render is responsible for taking the context (i.e., state) of the LiveView and generating the HTML/CSS for the -client. The LiveViewJS framework automatically passes the current context of the LiveView into render along with -meta data (things like the csrfToken, page url, etc.). It uses the html method to generate the HTML/CSS for the -client.

render Signature

render(context: TContext, meta: LiveViewMeta<TEvents>): LiveViewTemplate | Promise<LiveViewTemplate>;

The example render function below takes the count from the context and renders the HTML/CSS for the client:

counterLiveView.ts
import { createLiveView, html } from "liveviewjs";
/**
* A basic counter that increments and decrements a number.
*/
export const counterLiveView = createLiveView<
{ count: number }, // Define LiveView Context / State
{ type: "increment" } | { type: "decrement" } // Define LiveView Events
>({
// Setup / initialize the LiveView Context (i.e., set count to 0)
mount: (socket) => {
socket.assign({ count: 0 });
},
// Handle incoming increment and decrement events from User input
handleEvent: (event, socket) => {
const { count } = socket.context;
switch (event.type) {
case "increment":
socket.assign({ count: count + 1 });
break;
case "decrement":
socket.assign({ count: count - 1 });
break;
}
},
// Renders the Counter View based on the current Context / State
render: (context) => {
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});
info

You might have noticed the phx-click attributes present in the <button> elements in the example above. These -are examples of attributes (a.k.a "bindings") that are added to HTML elements that initiate server events based on user -interaction. There are four main types of bindings: click, form, key, focus/blur. We will cover them in more detail -in the section on User Events.

- - - - \ No newline at end of file diff --git a/docs/docs/category/anatomy-of-a-liveview.html b/docs/docs/category/anatomy-of-a-liveview.html deleted file mode 100644 index 0d0ca470..00000000 --- a/docs/docs/category/anatomy-of-a-liveview.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Anatomy of a LiveView | LiveViewJS - - - - -
-
- - - - \ No newline at end of file diff --git a/docs/docs/category/client-side-javascript.html b/docs/docs/category/client-side-javascript.html deleted file mode 100644 index 1295f8e4..00000000 --- a/docs/docs/category/client-side-javascript.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Client-side Javascript | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/forms--changesets.html b/docs/docs/category/forms--changesets.html deleted file mode 100644 index 32b29562..00000000 --- a/docs/docs/category/forms--changesets.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Forms & Changesets | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/js-commands.html b/docs/docs/category/js-commands.html deleted file mode 100644 index faf77fe1..00000000 --- a/docs/docs/category/js-commands.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -JS Commands | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/lifecycle-of-a-liveview.html b/docs/docs/category/lifecycle-of-a-liveview.html deleted file mode 100644 index 72b9882e..00000000 --- a/docs/docs/category/lifecycle-of-a-liveview.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Lifecycle of a LiveView | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/liveviewsocket.html b/docs/docs/category/liveviewsocket.html deleted file mode 100644 index 694385fc..00000000 --- a/docs/docs/category/liveviewsocket.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket | LiveViewJS - - - - -
-
- - - - \ No newline at end of file diff --git a/docs/docs/category/miscellaneous.html b/docs/docs/category/miscellaneous.html deleted file mode 100644 index 781abe5d..00000000 --- a/docs/docs/category/miscellaneous.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Miscellaneous | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/overview.html b/docs/docs/category/overview.html deleted file mode 100644 index e8696bcf..00000000 --- a/docs/docs/category/overview.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Overview | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/quick-starts.html b/docs/docs/category/quick-starts.html deleted file mode 100644 index 8aa64553..00000000 --- a/docs/docs/category/quick-starts.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Quick Starts | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/real-time--multi-player.html b/docs/docs/category/real-time--multi-player.html deleted file mode 100644 index 274a64f9..00000000 --- a/docs/docs/category/real-time--multi-player.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Real-time / Multi-player | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/uploading-files.html b/docs/docs/category/uploading-files.html deleted file mode 100644 index bf02b9c3..00000000 --- a/docs/docs/category/uploading-files.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Uploading Files | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/user-events.html b/docs/docs/category/user-events.html deleted file mode 100644 index 863177d9..00000000 --- a/docs/docs/category/user-events.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -User Events | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/category/webserver-integrations.html b/docs/docs/category/webserver-integrations.html deleted file mode 100644 index 1174e5dc..00000000 --- a/docs/docs/category/webserver-integrations.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Webserver Integrations | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/client-javascript/client-hooks.html b/docs/docs/client-javascript/client-hooks.html deleted file mode 100644 index 65413373..00000000 --- a/docs/docs/client-javascript/client-hooks.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - -"Hooks" (not the React kind) | LiveViewJS - - - - -
-

"Hooks" (not the React kind)

Sometimes you need to do something that is not supported by any of the existing user event bindings or that requires -hooking into a client-side DOM event. LiveView has "Hooks" for these types of situations.

caution

The term "Hooks" comes from Phoenix/LiveView which this project is based on and whose client library we are -utilizing. LiveView "Hooks" are in no way related to React "Hooks". It is unfortunate that "Hooks" is overloaded but we -don't find it very confusing considering how different LiveView is from React.

phx-hook Attribute

The phx-hook attribute is used to attach a LiveView "Hook" to a DOM element. The value of the attribute is the name of -the hook (which must be registered in the client-side LiveSocket). For example, if you wanted to attach a hook named -MyHook to a button, you would do the following:

<button phx-hook="MyHook">Click Me</button>

Registering Hooks

As noted above, you must register the hook in the client-side LiveSocket before you can use it. You do this by first -defining the hook and then by adding the hook to the hooks option when you create the LiveSocket:

// Define the hook
const Hooks = {
MyHook: {
mounted() {
// do something when the element is mounted
console.log("MyHook mounted");
}
}
}
...
// Add the hook to the LiveSocket
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { MyHook }
});
...

This hook will simply print a message to the console when the element is mounted.

Hook Lifecycle

Hooks have the following lifecycle methods:

  • mounted - the element has been added to the DOM and its server LiveView has finished mounting
  • beforeUpdate - the element is about to be updated in the DOM. Note: any call here must be synchronous as the -operation cannot be deferred or cancelled.
  • updated - the element has been updated in the DOM by the server
  • destroyed - the element has been removed from the page, either by a parent update, or by the parent being removed -entirely
  • disconnected - the element's parent LiveView has disconnected from the server
  • reconnected - the element's parent LiveView has reconnected to the server

Hook Context

Inside the hook lifecycle methods you can reference many additional properties and methods for the hook including:

  • el - attribute referencing the bound DOM node liveSocket - the reference to the underlying LiveSocket instance
  • pushEvent(event, payload, (reply, ref) => ...) - method to push an event from the client to the LiveView server
  • pushEventTo(selectorOrTarget, event, payload, (reply, ref) => ...) - method to push targeted events from the client -to LiveViews and LiveComponents. It sends the event to the LiveComponent or LiveView the selectorOrTarget is defined -in, where its value can be either a query selector or an actual DOM element. If the query selector returns more than -one element it will send the event to all of them, even if all the elements are in the same LiveComponent or LiveView.
  • handleEvent(event, (payload) => ...) - method to handle an event pushed from the server
  • upload(name, files) - method to inject a list of file-like objects into an uploader.
  • uploadTo(selectorOrTarget, name, files) - method to inject a list of file-like objects into an uploader. The hook -will send the files to the uploader with name defined by allow_upload/3 on the server-side. Dispatching new uploads -triggers an input change event which will be sent to the LiveComponent or LiveView the selectorOrTarget is defined in, -where its value can be either a query selector or an actual DOM element. If the query selector returns more than one -live file input, an error will be logged.
tip

The @types/phoenix_live_view package provides a (currently incomplete) type definition for a hook. You can use -it by importing the ViewHook type from the phoenix_live_view package.

import { ViewHook } from "phoenix_live_view";
- - - - \ No newline at end of file diff --git a/docs/docs/client-javascript/example-hook.html b/docs/docs/client-javascript/example-hook.html deleted file mode 100644 index 824de395..00000000 --- a/docs/docs/client-javascript/example-hook.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -Example Hook | LiveViewJS - - - - -
-

Example Hook

Let's create a hook that will format a text input into a phone number as a user types.

Hook Definition

// Define the hook
const PhoneNumber: ViewHook = {
mounted() {
this.el.addEventListener("input", (e) => {
let match = this.el.value.replace(/\D/g, "").match(/^(\d{3})(\d{3})(\d{4})$/);
if (match) {
this.el.value = `${match[1]}-${match[2]}-${match[3]}`;
}
});
},
};
// Add the hook to the LiveSocket
let liveSocket = new LiveSocket("/live", Socket, {
hooks: { PhoneNumber },
});

Hook Usage

<input phx-hook="PhoneNumber" type="text" />

Credit 🙌

Credit for this example goes to the -Phoenix LiveView docs. I didn't want -to reinvent the wheel, so I just copied the example from the Phoenix LiveView docs, added some types, and simplified it -a bit.

- - - - \ No newline at end of file diff --git a/docs/docs/client-javascript/overview.html b/docs/docs/client-javascript/overview.html deleted file mode 100644 index 80ffb034..00000000 --- a/docs/docs/client-javascript/overview.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - -Client-side Javascript | LiveViewJS - - - - -
-

Client-side Javascript

LiveViewJS pages do, in fact, require some client-side javascript to be loaded as part of the HTML page. This -client-side javascript handles parsing the phx-* attributes, connecting to the server (via websocket), appling the -diffs, and turning user interactions into events, among other things.

Let's look at the client-side javascript in more detail.

Default client-side JS:

The default Typescript that is compiled into the client-side javascript loaded by LiveViewJS is the following:

import NProgress from "nprogress";
import { Socket } from "phoenix";
import "phoenix_html";
import { LiveSocket } from "phoenix_live_view";

// Define the route that websockets will use to connect to your server
const url = "/live";

// Pull out the csrf token from the meta tag
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");

// Create the LiveSocket
let liveSocket = new LiveSocket(url, Socket, { params: { _csrf_token: csrfToken } });

// Show progress bar on live navigation and form submits (requires NProgress css)
window.addEventListener("phx:page-loading-start", (info) => NProgress.start());
window.addEventListener("phx:page-loading-stop", (info) => NProgress.done());

// connect if there are any LiveViews on the page
liveSocket.connect();

// If you want to expose liveSocket messages in the console for debugging, uncomment the following:
// liveSocket.enableDebug();

// If you want to simulate request latency, you can uncomment the following
// liveSocket.enableLatencySim(1000)

// finally add the liveSocket to the window
(window as any).liveSocket = liveSocket;
info

The libraries used in LiveViewJS are exactly the same libraries used in Phoenix LiveView. Beyond ensuring we -don't reinvent the wheel, this provides LiveViewJS with battle-tested, robust code that has proven extremely -reliable.

Walkthrough of the client-side JS

Let's walk through the client-side JS in more detail.

Imports

First, we are importing the NProgress library which is used to show a progress bar when the page is loading. We are -also importing the Socket and LiveSocket classes from the phoenix and phoenix_live_view libraries along with the -phoenix_html library.

import NProgress from "nprogress";
import { Socket } from "phoenix";
import "phoenix_html";
import { LiveSocket } from "phoenix_live_view";
...

LiveView Websocket Route

Next, we define the route that websockets will use to connect to your server. All websockets will be routed through this -URL. From there, LiveViewJS uses the page URL to determine which LiveView to route the connection to. If you change -this URL, you will need to change the server-side code that handles the websocket connection.

...
// Define the route that websockets will use to connect to your server
const url = "/live";
...

CSRF Token

Next, we pull out the CSRF token from the meta tag. This is used to authenticate the websocket connection.

...
// Pull out the csrf token from the meta tag
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
...

Create the LiveSocket

Then, we create the LiveSocket. This is the object that handles the websocket connection and all the LiveView logic. We -pass in the url, Socket, and any other options we want to configure the LiveSocket with.

...
// Create the LiveSocket
let liveSocket = new LiveSocket(url, Socket, { params: { _csrf_token: csrfToken } });
...
tip

The options that we can pass into the LiveSocket are:

  • bindingPrefix - the prefix to use for phoenix bindings. Defaults "phx-"
  • params - the connect_params to pass to the view's mount callback. May be a literal object or closure returning an -object. When a closure is provided, the function receives the view's element.
  • hooks – a reference to a user-defined hooks namespace, containing client callbacks for server/client interop. (We'll -cover Hooks in a later section)
  • uploaders – a reference to a user-defined uploaders namespace, containing client callbacks for client-side -direct-to-cloud uploads. (LiveViewJS currently does not support user-defined uploaders but we plan to add this in the -future)

Progress Bar

Next, we add event listeners to show a progress bar when the page is loading. This requires the NProgress library and -the NProgress css.

...
// Show progress bar on live navigation and form submits (requires NProgress css)
window.addEventListener("phx:page-loading-start", (info) => NProgress.start());
window.addEventListener("phx:page-loading-stop", (info) => NProgress.done());
...

You could swap NPorgress out for any other progress bar library you want like -topbar. If you did that you would have to update the event listeners to match the -events that your progress bar library uses.

Connect to the LiveSocket

Lastly, we connect to the LiveSocket. This will connect to the websocket and start the LiveView process.

...
// connect if there are any LiveViews on the page
liveSocket.connect();
...
// add the liveSocket to the window
(window as any).liveSocket = liveSocket;

Debugging and Latency Simulation

There are a couple of lines that are commented out by default. If you want to expose liveSocket messages in the console -for debugging or turn on latency simulation, you can uncomment the following lines:

...
// liveSocket.enableDebug();
// liveSocket.enableLatencySim(1000)
...

Default client-side JS is good start

For the most part, you shouldn't need to change the client-side JS especially at first. As you build more LiveViews you -might run into a need to do some more client-side DOM manipulation or handle events pushed from the server. In these -cases, you may need to add what is called a "Hook". We'll cover Hooks in the next section.

LiveView "Hooks" are a completely different concept from "Hooks" in React. LiveView Hooks are a way to add

custom client-side logic to your LiveView. Unfortunately, sometimes naming conflicts like this happen. Just remember -that LiveView Hooks are not the same as React Hooks. :::

- - - - \ No newline at end of file diff --git a/docs/docs/file-upload/drag-and-drop.html b/docs/docs/file-upload/drag-and-drop.html deleted file mode 100644 index 44f1f616..00000000 --- a/docs/docs/file-upload/drag-and-drop.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -Built-in Drag and Drop | LiveViewJS - - - - -
-

Built-in Drag and Drop

LiveViewJS ships with built-in support for drag and drop file uploads. It is incredibly easy to use. All you need to do -is add a <div /> that has the phx-drop-target attribute set to the upload config ref you want to target. For -example, if you want to allow users to drag and drop files into a photos upload config, you would do the following:

...
render: (context, meta) => {
...
<div phx-drop-target="${meta.uploads.photos.ref}">
Drop files here
</div>
...
}

That's it! LiveViewJS will automatically handle the rest. The user will be able to drag and drop files into the div -and they will be added to the entries of that upload config. 🤯

Credit where credit is due

Thanks to the Phoenix LiveView folks that built this! 🙌 This is a great example of why we built on top of the existing -LiveView client-side JS.

- - - - \ No newline at end of file diff --git a/docs/docs/file-upload/image-preview.html b/docs/docs/file-upload/image-preview.html deleted file mode 100644 index 962c9e60..00000000 --- a/docs/docs/file-upload/image-preview.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - -Built-in Image Preview | LiveViewJS - - - - -
-

Built-in Image Preview

LiveViewJS ships with build-in support for image previews when uploading files.

Getting Entries from meta.uploads

The list of UploadEntry objects for a given upload config can be found in the meta.uploads object based on the name -you provided when configuring it using the allowUpload method. For example, if you configured an upload config named -photos, you can access the list of UploadEntry objects using meta.uploads.photos. Here is an example of accessing -the list of UploadEntry objects for a given upload config:

...
render: (context, meta) => {
...
<div>
${meta.uploads.photos.map((entry) => {
return html`
<!-- render the entry -->
`;
})}
</div>
...
}

live_img_preview Tag

In order to use the built-in image preview, you must use the live_img_preview tag. This tag takes a UploadEntry and -renders an image preview of it.

...
render: (context, meta) => {
...
<div>${live_img_preview(entry)}</div>
...
}

All Together now

...
render: (context, meta) => {
...
<div>
${meta.uploads.photos.map((entry) => {
return html`
<div>${live_img_preview(entry)}</div>
`;
})}
</div>
...
}

That's it! 🤯

Credit where credit is due

Thanks to the Phoenix LiveView folks that built this! 🙌 This is a great example of why we built on top of the existing -LiveView client-side JS.

- - - - \ No newline at end of file diff --git a/docs/docs/file-upload/overview.html b/docs/docs/file-upload/overview.html deleted file mode 100644 index c26b21e4..00000000 --- a/docs/docs/file-upload/overview.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - -Overview | LiveViewJS - - - - -
-

Overview

File uploads are another common feature of web applications. LiveViewJS provides built in support for file uploads, -image previews, upload progress, drag and drop, error handling, and more. Handling file uploads can be intimidating, but -LiveViewJS makes it easy.

Learn by Example

We're going to start with a complete example and then walk through the code. The example LiveView allows you to create a -new photo album with a name and up to 3 photos.

info

This example is available as part of the packages/examples directory in the -LiveViewJS repository and runs on both the Express (NodeJS) and Oak (Deno) -servers.

Example LiveView Code

import {
createLiveView,
error_tag,
form_for,
html,
LiveViewChangeset,
live_file_input,
live_img_preview,
mime,
SingleProcessPubSub,
submit,
text_input,
UploadEntry,
} from "liveviewjs";
import { nanoid } from "nanoid";
import { z } from "zod";
import { InMemoryChangesetDB } from "../../datastore/InMemory";

type PhotosContext = {
photoGroups: PhotoGroup[];
changeset: LiveViewChangeset<PhotoGroup>;
};

type PhotosEvents =
| { type: "validate"; name: string }
| { type: "save"; name: string; urls: string[] }
| { type: "cancel"; config_name: string; ref: string };

export const photosLiveView = createLiveView<PhotosContext, PhotosEvents>({
mount: async (socket) => {
if (socket.connected) {
// listen to photos topic
await socket.subscribe(photoGroupTopic);
}
// setup the default context
socket.assign({
photoGroups: photoGroupStore.list(),
changeset: photoGroupStore.changeset(),
});
// configure the upload constraints
socket.allowUpload("photos", {
accept: [".png", ".jpg", ".jpeg", ".gif"], // only allow images
maxEntries: 3,
maxFileSize: 10 * 1024 * 1024, // 10MB
});
},
handleEvent: async (event, socket) => {
switch (event.type) {
case "validate": {
// just validate the changeset
socket.assign({ changeset: photoGroupStore.validate(event) });
break;
}
case "save": {
// first get the completed file uploads and map them to urls
// Note: the files are guaranteed to be completed here because
// save is the event called after all the uploads are complete
const { completed } = await socket.uploadedEntries("photos");

// set the urls on the event (which was not set via the form)
event.urls = completed.map(filename);

// attempt to save the photo
const photoCreate = photoGroupStore.create(event);
if (!photoCreate.valid) {
// if the photo is not valid, assign the changeset and return
// so that the form is re-rendered with the errors
socket.assign({
changeset: photoCreate,
});
return;
}
// Yay! We've successfully saved the photo, so we can consume (i.e., "remove")
// the uploaded entries from the "photos" upload config
await socket.consumeUploadedEntries("photos", async (meta, entry) => {
// we could create thumbnails, scan for viruses, etc.
// but for now move the data from the temp file (meta.path) to a public directory
meta.fileSystem.createOrAppendFile(`./public/${filename(entry)}`, meta.path);
});
// update the context with new photos and clear the form
socket.assign({
photoGroups: photoGroupStore.list(),
changeset: photoGroupStore.changeset(),
});
break;
}
case "cancel": {
const { config_name, ref } = event;
// remove the uploaded entry from the upload config
socket.cancelUpload(config_name, ref);
}
}
},
// Handle broadcast events from the pub/sub subscription for the "photoGroup" topic
handleInfo: (info, socket) => {
const { data } = info;
socket.assign({
photoGroups: [data],
changeset: photoGroupStore.changeset(),
});
},
// Render the view
render: (ctx, meta) => {
const { photoGroups, changeset } = ctx;
const { uploads } = meta;
return html`
<h2>My Photo Groups</h2>

<!-- Render the form -->
${form_for<PhotoGroup>("#", meta.csrfToken, {
id: "photo-form",
phx_change: "validate",
phx_submit: "save",
})}
<!-- photo group name input -->
<div>
Photo Group Name:
${text_input<PhotoGroup>(changeset, "name")}
${error_tag<PhotoGroup>(changeset, "name")}
</div>

<div>
<!-- file input / drag and drop -->
<div phx-drop-target="${uploads.photos.ref}" style="border: 2px dashed #ccc; padding: 10px; margin: 10px 0;">
${live_file_input(uploads.photos)}
or drag and drop files here
</div>
<!-- help text -->
<div style="font-size: 10px; padding-bottom: 3rem">
Add up to ${uploads.photos.maxEntries} photos
(max ${uploads.photos.maxFileSize / (1024 * 1024)} MB each)
</div>
</div>

<!-- any errors from the upload -->
${uploads.photos.errors?.map((error) => html`<p class="invalid-feedback">${error}</p>`)}

<!-- render the preview, progress, and cancel button of the selected files -->
${uploads.photos.entries.map(renderEntry)}

<!-- submit button -->
${submit("Upload", { phx_disable_with: "Saving...", disabled: uploads.photos.errors.length > 0 })}
</form>

<!-- render the photo groups -->
<ul id="photo_groups_list" phx-update="prepend">
${photoGroups.map(renderPhotoGroup)}
</ul>
`;
},
});

// Render a preview of the uploaded file with progress bar and cancel button
function renderEntry(entry: UploadEntry) {
return html`
<div style="display: flex; align-items: center;">
<div style="width: 250px; border: 1px solid black; margin: 2rem 0;">${live_img_preview(entry)}</div>
<div style="display: flex; align-items: center; margin-left: 2rem;">
<progress
style="position: relative; top: 8px; width: 150px; height: 1em;"
value="${entry.progress}"
max="100"></progress>
<span style="margin-left: 1rem;">${entry.progress}%</span>
</div>
<div style="display: flex; align-items: center;">
<a style="padding-left: 2rem;" phx-click="cancel" phx-value-config_name="photos" phx-value-ref="${entry.ref}"
>🗑</a
>
${entry.errors?.map((error) => html`<p style="padding-left: 1rem;" class="invalid-feedback">${error}</p>`)}
</div>
</div>
`;
}

// Render a photo group with a list of photos
function renderPhotoGroup(photoGroup: PhotoGroup) {
return html`<li id="${photoGroup.id}">
${photoGroup.urls.map(
(url, i) => html`
<h3>${photoGroup.name}(${i + 1})</h3>
<img src="${url}" />
`
)}
</li>`;
}

// Define the shape of the Photo type
const PhotoGroupSchema = z.object({
id: z.string().default(nanoid),
name: z.string().min(1).max(100),
urls: z.array(z.string()).min(1).default([]),
});

// Infer the type from the schema
type PhotoGroup = z.infer<typeof PhotoGroupSchema>;

// Pubsub topic for photos
const photoGroupTopic = "photoGroup";

// InMemory DB for photoGroup that publishes changes to the "photos" topic
const photoGroupStore = new InMemoryChangesetDB<PhotoGroup>(PhotoGroupSchema, {
pubSub: new SingleProcessPubSub(),
pubSubTopic: photoGroupTopic,
});

/**
* `filename` maps the upload entry to a filename based on the mime type of the entry
* concatenated with the entry's uuid
*/
function filename(entry: UploadEntry) {
const exts = mime.lookupExtensions(entry.client_type);
const ext = exts.length > 0 ? exts[0] : "bin";
return `${entry.uuid}.${ext}`;
}

Let's review each part in more detail to understand what's going on.

Configure the upload

First, we need to tell LiveView that we want to upload files and we use the socket.allowUpload method in mount to do -so:

mount: (socket) => {
...
// configure the upload constraints
socket.allowUpload("photos", {
accept: [".png", ".jpg", ".jpeg", ".gif"], // only allow images
maxEntries: 3,
maxFileSize: 10 * 1024 * 1024, // 10MB
});
...
}

The allowUpload method takes a config_name and an UploadConfig object. The config_name is used to identify the -upload config elsewhere in the LiveView lifecycle methods. More details on config options.

User Interface

There is a lot going on in our LiveView's render function so let's walk through that.

Setup the Form

As usual, we start by rendering the form with the form_for helper and set the phx-change and phx-submit events to -validate and save respectively.

...
${form_for<PhotoGroup>("#", meta.csrfToken, {
id: "photo-form",
phx_change: "validate",
phx_submit: "save",
})}
...

We will look at the handleEvent method later to see how especially the save event is handled.

File Input and Drag and Drop

Next, we need a place for the user to choose files for upload. We use the live_file_input helper to render the file -input and the phx-drop-target attribute to make the element a drop target for files. The phx-drop-target attribute -takes a ref which is used to identify the upload config in the LiveView lifecycle methods. You'll notice we are -referencing the uploads.photos config we configured in mount earlier.

...
<!-- file input / drag and drop -->
<div phx-drop-target="${uploads.photos.ref}" style="border: 2px dashed #ccc; padding: 10px; margin: 10px 0;">
${live_file_input(uploads.photos)}
or drag and drop files here
</div>
...
info

🤯 We just added a drag and drop target to our user interface with a single attribute (i.e. -phx-drop-target="${uploads.photos.ref}")! Pretty cool, right!? Thanks Phoenix LiveView team!! 🙌

caution

The live_file_input helper goes beyond just rendering the file input, it also adds some required attributes -to the file input and works with the rest of LiveViewJS to handle uploads. You should always use it rather than -rendering the file input yourself.

Dynamic Help Text

A very nice aspect of having the upload config available in render is it allows us to dynamically render help text -based on the upload config:

...
Add up to ${uploads.photos.maxEntries} photos
(max ${uploads.photos.maxFileSize / (1024 * 1024)} MB each)
...

Show preview, progress, and cancel for entries

When a user selects (or drags and drops) files for upload, the meta.uploads object is automatically updated with those -entries (and any errors for the upload or entries). We can use the upload.entries (and upload.errors) to show the -user what will be uploaded or what errors in their selections.

...
<!-- render the preview, progress, and cancel button of the selected files -->
${uploads.photos.entries.map(renderEntry)}
...

The renderEntry function shows the image preview using live_img_preview and the progress of the upload using a -progress element. We also render a cancel button using the phx-click event to cancel the upload.

🤯 Since we are allowing images only, we can use the live_img_preview helper to render a preview of the image

before it is uploaded. Again, pretty amazing that we get an image preview for free! Thanks Phoenix LiveView team!! 🙌

Show errors as well

We configured the file uploads to only allow certain image file types, limited the number of files to 3, and limited the -file size to 10MB. If the user selects files that don't meet these constraints, the uploads object will be updated -with the errors for the given config. We can use the upload.photos.errors to show the user what errors they have made -for the upload config and entry.errors to show the errors for a given entry.

...
<!-- render the errors for the upload config -->
${uploads.photos.errors?.map((error) => html`<p class="invalid-feedback">${error}</p>`)}
...
...
<!-- render the errors for the entry -->
${entry.errors?.map((error) => html`<p class="invalid-feedback">${error}</p>`)}
...

Whew, we've got some pretty amazing functionality in our UI and we haven't even uploaded any files yet! Let's look at -the LiveView lifecycle methods to see how we handle the uploads.

handleEvent Cases

handleEvent has two main events that it is handling for us: cancel, and save. Let's look at each of these in turn.

cancel event

A user may want to remove an entry from the setup of files they have selected. Perhaps the file is too large or the -wrong type or they've simply changed their mind. Our renderEntry function renders a cancel button next to each entry -that fires off the cancel event enabling the user to remove the entry from the upload.

...
handleEvent: (event, socket) => {
...
case "cancel": {
const { ref } = event;
// remove the entry from the upload config
socket.cancelUpload("photos", ref);
break;
}
...
}
...
note

A user can cancel an upload anytime before the socket.consumeUploadedEntries method is called.

save event

The save event is automatically fired when the user submits the form. In the case of file uploads, this event is not -sent to the handleEvent method until after all the files have been fully uploaded.

info

The upload progress for each entry will automatically be updated and the render method will be executed as -they are uploaded allowing us to show the user the progress of the upload.

Let's look at the save event handler:

...
handleEvent: (event, socket) => {
...
case "save": {
// first get the completed file uploads and map them to urls
// Note: the files are guaranteed to be completed here because
// save is the event called after all the uploads are complete
const { completed } = await socket.uploadedEntries("photos");

// set the urls on the event (which was not set via the form)
event.urls = completed.map(filename);

// attempt to save the photo
const photoCreate = photoGroupStore.create(event);
if (!photoCreate.valid) {
// if the photo is not valid, assign the changeset and return
// so that the form is re-rendered with the errors
socket.assign({
changeset: photoCreate,
});
return;
}
// Yay! We've successfully saved the photo, so we can consume (i.e., "remove")
// the uploaded entries from the "photos" upload config
await socket.consumeUploadedEntries("photos", async (meta, entry) => {
// we could create thumbnails, scan for viruses, etc.
// but for now move the data from the temp file (meta.path) to a public directory
meta.fileSystem.createOrAppendFile(`./public/${filename(entry)}`, meta.path);
});
// update the context with new photos and clear the form
socket.assign({
photoGroups: photoGroupStore.list(),
changeset: photoGroupStore.changeset(),
});
break;
}
...
}

It's pretty well commented but to summarize:

  1. We get the completed uploads from the photos upload config. (Note: the files are guaranteed to be completed here -because save is the event called only after all the uploads are complete).
  2. We map each entry to a url and add the urls to the event (which will become the photoGroup).
  3. We attempt to save the photoGroup and check if the changeset is valid. If not, we return here to show the errors -rather than consumeUploadedEntries.
  4. If the changeset is valid, we consumeUploadedEntries which will move the files from the temp directory to the -public directory and importantly, remove these files from the upload config.
  5. Finally, We update the context and clear the form.

Conclusion

Thanks for sticking with us through that. It was long and detailed and hopefully it was helpful. We think LiveViewJS -provides some pretty amazing out of the box support for file uploads.

- - - - \ No newline at end of file diff --git a/docs/docs/file-upload/upload-config-options.html b/docs/docs/file-upload/upload-config-options.html deleted file mode 100644 index 0cfc882f..00000000 --- a/docs/docs/file-upload/upload-config-options.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - -Upload Config Options | LiveViewJS - - - - -
-

Upload Config Options

These are the options you can pass into the allowUpload method to configure the upload.

  • accept: an array of strings that represent the file extensions and/or mime types that are allowed to be uploaded. -For example, [".png", ".jpg", ".jpeg", ".gif"] will only allow images to be uploaded. See -unique file type specifiers -for more information. Defaults to [] (no restrictions)
  • maxEntries: the maximum number of files that can be uploaded. If the user tries to upload more than this number of -files, an error will be present in the upload config. Defaults to 1.
  • maxFileSize: the maximum file size (in bytes) that can be uploaded. If the user tries to upload a file that is -larger than this number, an error will be present in the upload config. Defaults to 10 * 1024 * 1024 (10MB).
  • autoUpload: if true, the file will be uploaded as soon as it is selected by the user. If false, the file will -not be uploaded until the user initiates the form's save event. The default is false.
- - - - \ No newline at end of file diff --git a/docs/docs/forms-and-changesets/changesets.html b/docs/docs/forms-and-changesets/changesets.html deleted file mode 100644 index 0dbf41c8..00000000 --- a/docs/docs/forms-and-changesets/changesets.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - -Changesets | LiveViewJS - - - - -
-

Changesets

High Level

At a high level, Changesets are used to track creation and mutation of data models in LiveView projects and synchronize -them with HTML Forms and the user input that drives them.

Changeset are built on top of Zod which is a "Typescript-first schema validation -with static type inference" library.

Let's go through the steps of creating a changeset helper for a Book data model.

info

The basics of Zod are pretty easy to pick up especially if you -are familiar with Typescript. Even if you are not too familiar with Zod or Typescript, the concept is Zod is pretty -straight forward. Essentially you are defining a schema and then parsing the input data against the schema. If the input -data matches the schema then the data is valid. If the input data does not match the schema then the data is invalid and -you can use the error messages to display validation errors to the user.

Create a Zod Schema

First, we need to define a Zod schema for the data model:

import { z } from "zod";

// Use Zod to define the schema for the Book model
const BookSchema = z.object({
id: z.string().default(nanoid),
name: z.string().min(2).max(100),
author: z.string().min(2).max(100),
checked_out: z.boolean().default(false),
});

As you can see the BookSchema has an id, name, genre and checked_out property. The id is a string and is -generated using the nanoid function by default. The name and author are strings and must be at least 2 characters -long and no more than 100 characters long. The checked_out property is a boolean and is set to false by default.

Infer the Type based on the Schema

// infer the Book type from the schema
type Book = z.infer<typeof BookSchema>;

Now, we have a schema and a type for the Book data model. The z.infer function is used to infer a valid type from the -schema.

So far this is basic Zod usage. Now that we have the schema and type we can use them to create a -LiveViewChangesetFactory.

Generate a LiveViewChangesetFactory

import { newChangesetFactory } from "liveviewjs";

// generate changeset factory
const bookCSF = newChangesetFactory<Book>(BookSchema);

The newChangesetFactory function takes a Zod schema (and type annotation) and returns a LiveViewChangesetFactory for -that schema and type. The LiveViewChangesetFactory is used to create LiveViewChangesets for the Book model.

Using a LiveViewChangesetFactory

Despite it's long name, a LiveViewChangesetFactory is simply a function that takes two Partials and an (optional) -action string and returns a LiveViewChangeset. The first Partial is the starting data model and the second Partial -is the changes to the data model.

Here is the signature of the LiveViewChangesetFactory:

/**
* A factory for creating a changeset for a given existing data model, updated data model, and optional action.
*/
export type LiveViewChangesetFactory<T> = (
existing: Partial<T>,
newAttrs: Partial<T>,
action?: string
) => LiveViewChangeset<T>;

When you generate a LiveViewChangesetFactory for a given schema and type, the LiveViewChangesetFactory is created -(and "typed") for that schema and type. So, if you generate a LiveViewChangesetFactory for a Book schema and type, -then the LiveViewChangesetFactory will be typed to a LiveViewChangeset for a Book model and will only accept -Partial<Book>s as the first and second arguments.

Use a LiveViewChangesetFactory to create a LiveViewChangeset

Now that we have a LiveViewChangesetFactory for the Book model, we can use it to create a LiveViewChangeset for a -Book data.

const bookData: Partial<Book> = {
name: "The Hobbit",
author: "J.R.R. Tolkien",
};
// create a new changeset for a new book
const bookCS = changeset({}, bookData, "create");

The bookCS object is a LiveViewChangeset for a Book model. Zod parsed and validated the bookData we passed in.

A LiveViewChangeset has the following properties:

  • valid: boolean - does the resulting merged data model pass all validation rules?
  • changes: Partial<T> - just the parts of the model that have been changed relative to the existing model
  • data: T - the data model after the changeset has been applied (which may be invalid or valid depending on the -validations)
  • errors: { [Property in keyof T]?: string | undefined; } - an object that maps property names to error messages if -the changeset has invalid data
  • action?: string - the changeset action (or undefined if unset)

So now that we've created a LiveViewChangeset for the book data we can ask it if the data is valid or not:

bookCS.valid; // => true

Then we can ask it for the new data:

bookCS.data; // => { id: "some-random-id", name: "The Hobbit", author: "J.R.R. Tolkien", checked_out: false }

Invalid Data

Let's create a new LiveViewChangeset for a book with invalid data:

const invalidBookData: Partial<Book> = {
name: "a",
author: "b",
};
// create a new changeset for this book
const invalidBookCS = changeset({}, invalidBookData, "create");

Now, we can ask the invalidBookCS if it is valid:

invalidBookCS.valid; // => false

And we can ask it for the errors:

invalidBookCS.errors; // => { name: "Must be at least 2 characters", author: "Must be at least 2 characters" }

What about the action string?

The value of the action string has no significance in an of itself. The presence of the action string however does -impact the valid property of the LiveViewChangeset returned by the LiveViewChangesetFactory. If the action -string is NOT set the valid property will always return true. If the action string IS set the valid property -will return true if the data is valid and false if the data is invalid.

note

We pass "empty" (i.e., no action string) changesets to form helpers in the render function otherwise there -would be errors on the form when the page is first loaded. "Empty" changesets are always valid.

Next Steps

Now that you understand "Changesets", we can show you how they are powerful partners with the Form Events we discussed -earlier.

- - - - \ No newline at end of file diff --git a/docs/docs/forms-and-changesets/overview.html b/docs/docs/forms-and-changesets/overview.html deleted file mode 100644 index a3497b9e..00000000 --- a/docs/docs/forms-and-changesets/overview.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - -Overview | LiveViewJS - - - - -
-

Overview

Forms are obviously extremely important for any web application that needs user input. Building, validating, and -handling form submission is built into LiveViewJS forms .

Form Bindigs

We've already reviewed the form event bindings that are available in LiveViewJS. Here is a quick summary:

  • phx-change - This event is sent to the server along with all the form values when any form input is changed.
  • phx-submit - This event is sent to the server when the form is submitted alog with all the form values.

Feel free to review form events in more detail in the -User Events and Bindings section.

Changesets

We have not yet discussed the concept of a "changeset" in LiveViewJS. At a high level a changeset is a way to parse and -validate that incoming JSON data maps to the expected constraints. You will see it is a very powerful concept that -allows you to build and validate complex forms with ease.

note

Changesets are a concept that is taken from an Elixir library called -Ecto. Ecto changesets are used to validate and persist data to a -database. While LiveViewJS changeset are not ORM or DB releated, we've taken the concept of a changeset and adapted -it to the Typescript world for parsing and validation.

We will take a deep dive into Changesets in a more detail in the next section.

- - - - \ No newline at end of file diff --git a/docs/docs/forms-and-changesets/use-with-forms.html b/docs/docs/forms-and-changesets/use-with-forms.html deleted file mode 100644 index 9c23b864..00000000 --- a/docs/docs/forms-and-changesets/use-with-forms.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - -Forms & Changesets Example | LiveViewJS - - - - -
-

Forms & Changesets Example

Now that we've revisited Form Events and learned about Changesets, let's take a look at how we can use them together to -build, validate, and process a form.

Example LiveView

We're going to build a LiveView for managing our book library. We'll be able to add new books and mark those books as -"available" or "checked out".

Here's the LiveView example code:

import {
createLiveView,
error_tag,
form_for,
html,
LiveViewChangeset,
newChangesetFactory,
submit,
text_input,
} from "liveviewjs";
import { nanoid } from "nanoid";
import { z } from "zod";

// Create the zod BookSchema
const BookSchema = z.object({
id: z.string().default(nanoid),
name: z.string().min(2).max(100),
author: z.string().min(2).max(100),
checked_out: z.boolean().default(false),
});

// Infer the Book type from the BookSchema
type Book = z.infer<typeof BookSchema>;

// in memory data store for Books
const booksDB: Record<string, Book> = {};

// Book LiveViewChangesetFactory
const bookCSF = newChangesetFactory<Book>(BookSchema);

export const booksLiveView = createLiveView<
// Define the Context of the LiveView
{
books: Book[];
changeset: LiveViewChangeset<Book>;
},
// Define events that are initiated by the end-user
| { type: "save"; name: string; author: string }
| { type: "validate"; name: string; author: string }
| { type: "toggle-checkout"; id: string }
>({
mount: async (socket) => {
socket.assign({
books: Object.values(booksDB),
changeset: bookCSF({}, {}), // empty changeset
});
},

handleEvent: (event, socket) => {
switch (event.type) {
case "validate":
// validate the form data
socket.assign({
changeset: bookCSF({}, event, "validate"),
});
break;
case "save":
// attempt to create the volunteer from the form data
const saveChangeset = bookCSF({}, event, "save");
let changeset = saveChangeset;
if (saveChangeset.valid) {
// save the book to the in memory data store
const newBook = saveChangeset.data as Book;
booksDB[newBook.id] = newBook;
// since book was saved, reset the changeset to empty
changeset = bookCSF({}, {});
}
// update context
socket.assign({
books: Object.values(booksDB),
changeset,
});
break;
case "toggle-checkout":
// lookup book by id
const book = booksDB[event.id];
if (book) {
// update book
book.checked_out = !book.checked_out;
booksDB[book.id] = book;
// update context
socket.assign({
books: Object.values(booksDB),
});
}
break;
}
},

render: (context, meta) => {
const { changeset, books } = context;
const { csrfToken } = meta;
return html`
<h1>My Library</h1>

<div id="bookForm">
${form_for<Book>("#", csrfToken, {
phx_submit: "save",
phx_change: "validate",
})}

<div class="field">
${text_input<Book>(changeset, "name", { placeholder: "Name", autocomplete: "off", phx_debounce: 1000 })}
${error_tag(changeset, "name")}
</div>

<div class="field">
${text_input<Book>(changeset, "author", { placeholder: "Author", autocomplete: "off", phx_debounce: 1000 })}
${error_tag(changeset, "author")}
</div>

${submit("Add Book", { phx_disable_with: "Saving..." })}
</form>
</div>

<div id="books">
${books.map(renderBook)}
</div>
`;
},
});

function renderBook(b: Book) {
const color = b.checked_out ? `color: #ccc;` : ``;
const emoji = b.checked_out ? `📖[checked out]` : `📚[available]`;
return html`
<div id="${b.id}" style="margin-top: 1rem; ${color}">
${emoji}<span>${b.name}</span> by <span>${b.author}</span>
<div>
<button phx-click="toggle-checkout" phx-value-id="${b.id}" phx-disable-with="Updating...">
${b.checked_out ? "Return" : "Check Out"}
</button>
</div>
</div>
`;
}

Code Walk Through

Let's walk through the code to see how it all works together.

First defining the Zod Schema, Types, and Changeset Factory

// Create the zod BookSchema
const BookSchema = z.object({
id: z.string().default(nanoid),
name: z.string().min(2).max(100),
author: z.string().min(2).max(100),
checked_out: z.boolean().default(false),
});

// Infer the Book type from the BookSchema
type Book = z.infer<typeof BookSchema>;

// Book LiveViewChangesetFactory
const bookCSF = newChangesetFactory<Book>(BookSchema);

This code should look familiar. We're defining the Zod Schema, inferring the type, and creating the changeset factory -for the BookSchema and Book type.

Define the LiveView TContext and TEvent types

...
export const booksLiveView = createLiveView<
// Define the Context of the LiveView
{
books: Book[];
changeset: LiveViewChangeset<Book>;
},
// Define events that are initiated by the end-user
| { type: "save"; name: string; author: string }
| { type: "validate"; name: string; author: string }
| { type: "toggle-checkout"; id: string }
>({
...

Here we are saying the context of our LiveView will have a books array of type Book and a changeset of type -LiveViewChangeset<Book>. We are also defining the events that can be initiated by the end-user. In this case, we -have save, validate, and toggle-checkout events along with the data that is required for each event.

Use Helpers in render

...
render: (context, meta) => {
// get the changeset and books from the context
const { changeset, books } = context;
// pull out the csrf token from the meta data
const { csrfToken } = meta;
return html`
<h1>My Library</h1>

<div id="bookForm">
<!-- use the form_for helper to render the form -->
${form_for<Book>("#", csrfToken, {
phx_submit: "save",
phx_change: "validate",
})}

<div class="field">
<!-- use the text_input helper to render the input for the name property -->
${text_input<Book>(changeset, "name", { placeholder: "Name", autocomplete: "off", phx_debounce: 1000 })}
<!-- use the error_tag helper to optionally render the error message for the name property -->
${error_tag(changeset, "name")}
</div>

<div class="field">
<!-- use the text_input helper to render the input for the author property -->
${text_input<Book>(changeset, "author", { placeholder: "Author", autocomplete: "off", phx_debounce: 1000 })}
<!-- use the error_tag helper to optionally render the error message for the author property -->
${error_tag(changeset, "author")}
</div>
<!-- use the submit helper to render the submit button -->
${submit("Add Book", { phx_disable_with: "Saving..." })}
</form>

</div>

<div id="books">
${books.map(renderBook)}
</div>
`;
},
...

There is a bit going on here so let's break it down. First we are pulling out the changeset and books from the -context. We are also pulling out the csrfToken from the meta data. Next we are using the form_for helper to -render the form. We are passing in the changeset and the csrfToken as well as setting the phx_submit and -phx_change attributes to the events that will be called in handleEvent.

tip

Using the form_for helper is optional. You can use the form helper and set the phx_submit and phx_change -attributes to the events that will be called in handleEvent. The form_for helper just makes it a little easier to -render the form and automatically passes the csrfToken to the form.

The LiveViewJS framework automatically validates the csrfToken (a.k.a. authenticity token) for you and will throw an -error if the token is invalid.

Next we are using the text_input helper to render the input for the name property. We are passing in the changeset -and the name property as well as the placeholder, autocomplete, and phx_debounce attributes.

tip

The text_input helper is optional but it provides type safefy and automatically works with the changeset. Of -note, we use the phx-debounce attribute to only send the change event to the server after the user has stopped typing -for 1000ms. This is a great way to reduce the number of events sent to the server and improve performance.

Next we are using the error_tag helper to optionally render the error message for the name property. We are passing -in the changeset and the name property there as well.

tip

The error_tag helper is optional but it provides type safefy and automatically works with the changeset to -pull out the error message for the property if there is one.

We follow the same pattern for the author property.

Next, we are using the submit helper to render the submit button. We are passing in the Add Book text and the -phx_disable_with attribute.

Finally we map over the books to render each book using the renderBook function.

Handle validate event

handleEvent: (event, socket) => {
...
case "validate":
// validate the form data
socket.assign({
changeset: bookCSF({}, event, "validate"),
});
break;
...

When handleEvent occurs with the validate event, we use the bookCSF changeset factory to generate a new -LiveViewChangeset for the the form data (also present in the event). Since, this isn't updating the book, we pass an -empty object {} first, along with the event data. Importantly, we set the action to validate and assign the -result to the changeset in the context.

info

Remember if you don't set the action text in the LiveViewChangesetFactory call then returned -LiveViewChangeset will always be valid.

The LiveViewChangeset works with the helpers in render to automatically render the error messages for the properties -that have errors and set the value of the input fields to the values that were submitted.

Handle save event

handleEvent: (event, socket) => {
...
case "save":
// attempt to create the volunteer from the form data
const saveChangeset = bookCSF({}, event, "save");
let changeset = saveChangeset;
if (saveChangeset.valid) {
// save the book to the in memory data store
const newBook = saveChangeset.data as Book;
booksDB[newBook.id] = newBook;
// since book was saved, reset the changeset to empty
changeset = bookCSF({}, {});
}
// update context
socket.assign({
books: Object.values(booksDB),
changeset,
});
break;
...

When handleEvent occurs with the save event, we use the bookCSF changeset factory to generate a new -LiveViewChangeset for the the form data (also present in the event). Since, this is also not updating an existing -book, we pass an empty object {} first, along with the event data. Importantly, we set the action to save and -assign the result to the saveChangeset variable.

If the saveChangeset is valid, we save the book to the in memory data store. We then reset the changeset to be an -empty changeset (i.e., bookCSF({}, {})). If the saveChangeset is not valid, we keep the changeset as the -saveChangeset so that the error messages will be rendered in the form using the form helpers (i.e., error_tag and -text_input).

Finally, we update the books and changeset in the context.

Bonus toggle_checkout event

handleEvent: (event, socket) => {
...
case "toggle_checkout":
// lookup book by id
const book = booksDB[event.id];
if (book) {
// update book
book.checked_out = !book.checked_out;
booksDB[book.id] = book;
// update context
socket.assign({
books: Object.values(booksDB),
});
}
break;
...

When handleEvent occurs with the toggle_checkout event, we toggle the checked_out property of the book in the in -memory data store and update the books in the context.

Conclusion

As you can see, Forms and Changesets are a powerful way to build forms in LiveView. They provide type safety and -automatically render the error messages and input values. They validate the form data incrementally and upon submission. -In short, they make forms easy and fun again!

- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/add-remove-class.html b/docs/docs/js-commands/add-remove-class.html deleted file mode 100644 index 90777609..00000000 --- a/docs/docs/js-commands/add-remove-class.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - -Add/Remove Class Commands | LiveViewJS - - - - -
-

Add/Remove Class Commands

Add or remove css classes including optional transition classes from an element using the add_class and remove_class -commands.

Add Class Command

The add_class command adds one or more css classes to an element.

new JS().add_class(names: string, options?: ClassOptions)
  • names - A string of space separated css class names to add to the element
  • options - Options for the command (optional)
    • to - A css selector to identify the element to add the class to. Defaults to the element that the JS Command is -attached to.
    • time - The time (in milliseconds) over which to apply the transition options. Defaults to 200
    • transition - The string of classes to apply before adding the classes, or a 3-tuple containing the transition -class, the class to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out -duration-300", "opacity-0", "opacity-100"]

Examples

//... in your render function of a LiveView

// add the "bg-blue-500" class to this button element on click
<button phx-click="${new JS().add_class("bg-blue-500")}">Add Class</button>

// add the "bg-blue-500" class to the element with id="target" on click
<button phx-click="${new JS().add_class("bg-blue-500", {to: "#target"})}">Add Class</button>
<div id="target">My Target</div>

// add the "bg-blue-500" class to the element with id="target2" on click with a transition over 400ms
<button phx-click="${new JS().add_class("bg-blue-500", {to: "#target2", transition: ["ease-out duration-300", "opacity-0", "opacity-100"], time: 400})}">Add Class</button>
<div id="target2">My Target2</div>

Remove Class Command

The remove_class command removes one or more css classes from an element.

new JS().remove_class(names: string, options?: ClassOptions)
  • names - A string of space separated css class names to remove from the element
  • options - Options for the command (optional)
    • to - A css selector to identify the element to remove the class from. Defaults to the element that the JS Command -is attached to.
    • time - The time (in milliseconds) over which to apply the transition options. Defaults to 200
    • transition - The string of classes to apply before removing the classes, or a 3-tuple containing the transition -class, the class to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out -duration-300", "opacity-0", "opacity-100"]

Examples

//... in your render function of a LiveView

// remove the "bg-blue-500" class from this button element on click
<button class="bg-blue-500" phx-click="${new JS().remove_class("bg-blue-500")}">Remove Class</button>

// remove the "bg-blue-500" class from the element with id="target" on click
<button phx-click="${new JS().remove_class("bg-blue-500", {to: "#target"})}">Remove Class</button>
<div id="target" class="bg-blue-500">My Target</div>

// remove the "bg-blue-500" class from the element with id="target2" on click with a transition over 400ms
<button phx-click="${new JS().remove_class("bg-blue-500", {to: "#target2", transition: ["ease-out duration-300", "opacity-0", "opacity-100"], time: 400})}">Remove Class</button>
<div id="target2" class="bg-blue-500">My Target2</div>
- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/dispatch-cmd.html b/docs/docs/js-commands/dispatch-cmd.html deleted file mode 100644 index 543112df..00000000 --- a/docs/docs/js-commands/dispatch-cmd.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -Dispatch Command | LiveViewJS - - - - -
-

Dispatch Command

The dispatch command dispatches a DOM event from the target element

new JS().dispatch(event: string, options?: DispatchOptions)
  • event - The name of the event to dispatch
  • options - Options for the command (optional)
    • to - An optional css selector to identify the element from which to dispatch. Defaults to the element that the JS -Command is attached to.
    • detail - A optional map of key/value pairs to include in the event's detail property
    • bubbles - A optional boolean indicating whether the event should bubble up the DOM. Defaults to true

Note: All events dispatched are of a type CustomEvent, with the exception of "click". For a "click", a MouseEvent is -dispatched to properly simulate a UI click.

For emitted CustomEvent's, the event detail will contain a dispatcher, which references the DOM node that dispatched the -JS event to the target element.

Examples

//... in your render function of a LiveView

// dispatch a click
<button phx-click="${new JS().dispatch("click", { to: "#dispatch" })}">Dispatch Click</button>

// dispatch a custom event
<button phx-click="${new JS().dispatch("custom", { to: "#dispatch", detail: { foo: "bar" } })}">
Dispatch Custom
</button>
<div id="dispatch">Dispatch Target</div>
- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/overview.html b/docs/docs/js-commands/overview.html deleted file mode 100644 index dc5bf6c0..00000000 --- a/docs/docs/js-commands/overview.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -Overview | LiveViewJS - - - - -
-

Overview

LiveViewJS user events (clicks, etc) typically trigger a server-side event which updates the state of the LiveView and -re-renders the HTML. Sometimes you want to update the DOM without (or in addition to) initiating a server round trip. -This is where JS Commands come in.

JS Commands support a number of client-side DOM manipulation function that can be used to update the DOM without a -server round trip. These functions are:

  • add_class - Add css classes to an element including optional transition classes
  • remove_class - Remove css classes from an element including optional transition classes
  • set_attribute - Set an attribute on an element
  • remove_attribute - Remove an attribute from an element
  • show - Show an element including optional transition classes
  • hide - Hide an element including optional transition classes
  • toggle - Toggle the visibility of an element
  • dispatch - Dispatch a DOM event from an element
  • transition - Apply transition classes to an element (i.e., animate it)
  • push - Push an event to the LiveView server (i.e., trigger a server round trip)

JS Command Syntax

JS Commands are used in the render function of a LiveView or LiveComponent:

import { JS } from 'lieviewjs';

//... render function of a LiveView
render() {
return html`
<div>
<button phx-click="${new JS().toggle({ to: "#toggle" })}">Toggle</button>
<div id="toggle">Toggle Me</div>
</div>
`;
}

"Chainable" (i.e., fluent) Syntax

JS Commands are "chainable" (i.e., fluent) so you can chain multiple commands together as needed and they will be -executed in the order they are called:

import { JS } from 'lieviewjs';

//... render function of a LiveView
render() {
// hide the button then push the "increment" event to the server
return html`
<div>
<button phx-click="${new JS().hide().push("increment")}">Increment</button>
</div>
`;
}

Example JS Commands LiveView

See packages/examples/src/liveviews/jsCommands for a working, complete set of examples of using JS Commands.

More details on each JS Command can be found in the following sections.

- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/push-cmd.html b/docs/docs/js-commands/push-cmd.html deleted file mode 100644 index 702ae820..00000000 --- a/docs/docs/js-commands/push-cmd.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -Push Command | LiveViewJS - - - - -
-

Push Command

The push command sends an event to the server

new JS().push(event: string, options?: PushOptions)
  • event - The name of the event to send to the server
  • options - Options for the command (optional)
    • target - An optional selector or component ID to push to
    • loading - An optional selector to apply the phx loading classes to
    • page_loading - An optional boolean indicating whether to trigger the "phx:page-loading-start" and -"phx:page-loading-stop" events. Defaults to false
    • value An optional map of key/value pairs to include in the event's value property

Examples

//... in your render function of a LiveView

// push increment event to server
<button phx-click="${new JS().push("increment")}">+</button>

// push decrement event to server
<button phx-click="${new JS().push("decrement")}">-</button>

// push increment event to server with a payload then hide the button
<button phx-click="${new JS().push("increment", value: {foo: "bar"}).hide()}">
Increment then hide
</button>

// hide the button then push increment event
<button phx-click="${new JS().hide().push("increment")}">Hide then Increment</button>

// push incremenet and show page loading indicator
<button phx-click="${new JS().push("increment", { page_loading: true })}">
Page Loading Push
</button>
- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/set-remove-attr.html b/docs/docs/js-commands/set-remove-attr.html deleted file mode 100644 index 61847916..00000000 --- a/docs/docs/js-commands/set-remove-attr.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - -Set/Remove Attribute Commands | LiveViewJS - - - - -
-

Set/Remove Attribute Commands

Set or remove an attribute from an HTML element using the set_attribute and remove_attribute commands.

Set Attribute Command

The set_attribute command add or updates a single attribute on the target element.

new JS().set_attribute(attr: [string, string], options?: AttributeOptions)
  • attr - the 2-tuple of the attribute name and value to set
  • options - Options for the command (optional)
    • to - A css selector to identify the element to set the attribute on. Defaults to the element that the JS Command -is attached to.

Examples

//... in your render function of a LiveView

// set the "disabled" attribute to on the target button element on click
<button phx-click="${new JS().set_attribute(["disabled", ""], { to: "#target" })}">Set Disabled</button>
<button id="target">Target</button>

// set the "aria-expanded" attribute to "true" on the target element on click
<button phx-click={new JS().set_attribute(["aria-expanded", "true"], to: "#dropdown")}>
Expand Dropdown
</button>
<div id="dropdown" aria-expanded="false">Dropdown</div>

Remove Attribute Command

The remove_attribute command removes a single attribute from the target element.

new JS().remove_attribute(attr: string, options?: AttributeOptions)
  • attr - the attribute name to remove
  • options - Options for the command (optional)
    • to - A css selector to identify the element to remove the attribute from. Defaults to the element that the JS -Command is attached to.

Examples

//... in your render function of a LiveView

// remove the "disabled" attribute from the target button element on click
<button phx-click="${new JS().remove_attribute("disabled", { to: "#target" })}">Remove Disabled</button>
<button id="target" disabled>Target</button>

// remove the "aria-expanded" attribute from the target element on click
<button phx-click={new JS().remove_attribute("aria-expanded", to: "#dropdown")}>
Close Dropdown
</button>
<div id="dropdown" aria-expanded="true">Dropdown</div>
- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/show-hide-toggle-el.html b/docs/docs/js-commands/show-hide-toggle-el.html deleted file mode 100644 index d423cfb8..00000000 --- a/docs/docs/js-commands/show-hide-toggle-el.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - -Show/Hide/Toggle Element Commands | LiveViewJS - - - - -
-

Show/Hide/Toggle Element Commands

The show, hide, and toggle commands are used to show, hide, or toggle the visibility of an element including css -transition classes. The element is identified by a CSS selector.

Show Command

The show command makes the target element visible

new JS().show(options?: ShowOptions)
  • options - Options for the command (optional)
    • to - A css selector to identify the element make visible. Defaults to the element that the JS Command is attached -to.
    • time - The time (in milliseconds) over which to apply the transition options. Defaults to 200
    • transition - The string of classes to apply before showing the element, or a 3-tuple containing the transition -class, the class to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out -duration-300", "opacity-0", "opacity-100"]
    • display - The display type to apply to the element. Defaults to "block"

Examples

//... in your render function of a LiveView

// show the target element on click
<button phx-click="${new JS().show({ to: "#show_me" })}">Show</button>
<div id="show_me" style="display: none;">Show Me</div>

// show the target element with a transition on click
<button phx-click="${new JS().show({
to: "#show_me2",
transition: ["ease-out duration-300", "opacity-0", "opacity-100"],
time: 400
})}">Show w/ Transition</button>
<div id="show_me2" style="display: none;">Show Me2</div>

Hide Command

The hide command makes the target element hidden

new JS().hide(options?: ShowOptions)
  • options - Options for the command (optional)
    • to - A css selector to identify the element to hide. Defaults to the element that the JS Command is attached to.
    • time - The time (in milliseconds) over which to apply the transition options. Defaults to 200
    • transition - The string of classes to apply before hiding the element, or a 3-tuple containing the transition -class, the class to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out -duration-300", "opacity-100", "opacity-0"]

Examples

//... in your render function of a LiveView

// hide the target element on click
<button phx-click="${new JS().hide({ to: "#hide_me" })}">Hide</button>
<div id="hide_me">Hide Me</div>

// hide the target element with a transition on click
<button phx-click="${new JS().hide({
to: "#hide_me",
transition: ["ease-out duration-300", "opacity-100", "opacity-0"],
time: 400
})}">Hide w/ Transition</button>
<div id="hide_me2">Hide Me2</div>

Toggle Command

The toggle command toggles the visibility of the target element

new JS().toggle(options?: ToggleOptions)
  • options - Options for the command (optional)
    • to - A css selector to identify the element to toggle. Defaults to the element that the JS Command is attached to.
    • time - The time (in milliseconds) over which to apply the transition options. Defaults to 200
    • in - The string of classes to apply when toggling to visible, or a 3-tuple containing the transition class, the -class to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out duration-300", -"opacity-0", "opacity-100"]
    • out - The string of classes to apply when toggling to hidden, or a 3-tuple containing the transition class, the -class to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out duration-300", -"opacity-100", "opacity-0"]
    • display - The display type to apply to the element when toggling to visible. Defaults to "block"

Examples

//... in your render function of a LiveView

// toggle the target element on click
<button phx-click="${new JS().toggle({ to: "#toggle_me" })}">Toggle</button>
<div id="toggle_me">Toggler</div>

// toggle the target element with a transition in/out on click
<button phx-click="${new JS().toggle({
to: "#toggle_me2",",
in: ["ease-out duration-300", "opacity-0", "opacity-100"],
out: ["ease-out duration-300", "opacity-100", "opacity-0"],
time: 400
})}">Toggle w/ Transition</button>
<div id="toggle_me2">Toggle Me 2</div>
- - - - \ No newline at end of file diff --git a/docs/docs/js-commands/transition-cmd.html b/docs/docs/js-commands/transition-cmd.html deleted file mode 100644 index 52784e84..00000000 --- a/docs/docs/js-commands/transition-cmd.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -Transition Command | LiveViewJS - - - - -
-

Transition Command

The transition command dispatches a DOM event from the target element

new JS().transition(transition: Transition, options?: TransitionOptions)
  • transition - The string of classes to apply to the element, or a 3-tuple containing the transition class, the class -to apply to start the transition, and the class to apply to end the transition. e.g., ["ease-out duration-300", -"opacity-100", "opacity-0"]
  • options - Options for the command (optional)
    • to - An optional css selector to identify the element from which to transition. Defaults to the element that the -JS Command is attached to.
    • time - The time (in milliseconds) over which to apply the transition options. Defaults to 200

Examples

//... in your render function of a LiveView

// transition the target element
<button
phx-click="${new JS()
.transition("fade-in-scale", {
to: "#transition",
})
.show({ to: "#transition", transition: "fade-in-scale" })}">
Transition In
</button>
<button
phx-click="${new JS()
.transition("fade-out-scale", {
to: "#transition",
})
.hide({ to: "#transition", transition: "fade-out-scale" })}">
Transition Out
</button>
<div id="transition">Transition Target</div>

// transition button with a shake
<button phx-click="${new JS().transition("shake")}">Shake</button>
- - - - \ No newline at end of file diff --git a/docs/docs/lifecycle-of-a-liveview/intro.html b/docs/docs/lifecycle-of-a-liveview/intro.html deleted file mode 100644 index 83540a5f..00000000 --- a/docs/docs/lifecycle-of-a-liveview/intro.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - -Lifecycle of a LiveView | LiveViewJS - - - - -
-

Lifecycle of a LiveView

We are going to look at the lifecycle of LiveViews in detail to see when each LiveView method (e.g., mount, -handleEvent, render, etc) are called during the lifecycle so you can better understand how to use them.

HTTP and Websocket

There are two major parts of a lifecycle:

  1. HTTP request phase
  2. Websocket phase

HTTP Request Phase

Just like any other web page, all LiveViews start with a HTTP request to a URL (e.g., GET /my-liveview) and this route -is served by a webserver (e.g., Express). If that route is a LiveView route, the webserver hands off the request to the -LiveViewJS library for processing.

The LiveViewJS library then creates a new LiveView instance and starts the HTTP request phase which consists of:

  1. mount - The LiveView is mounted and the context is initialized
  2. handleParams - The LiveView is given a chance to handle any params passed in the URL (e.g. -GET /my-liveview?foo=bar)
  3. render - The LiveView is rendered based on the context and the HTML is returned to the webserver

The webserver then returns the HTML to the browser. Below is a sequence diagram showing the HTTP request phase:

Http Request Phase

Advantages of HTML

The advantage of rendering the HTML initially is:

  • "First paint" is extremely fast (it's just HTML)
  • No waiting for MBs of JS to download
  • Renders even if JS is disabled
  • Search engine friendly (again it is only HTML)

Websocket Phase

After the initial HTTP request and response, the LiveView client javascript automatically connects to the LiveView -server via a websocket. The websocket is used to send events from the browser to the LiveView server and to receive DOM -patches from the LiveView server. The websocket phase breaks down into three parts:

  1. "Websocket Join" - Establish the websocket connection and run the initial LiveView lifecycle methods
  2. "Interactive" - The LiveView is interactive and can respond to user events or update based on server events
  3. "Shutdown" - The LiveView automatically cleans up and shuts down resources

Websocket Join Phase

During the "Websocket Join" phase the LiveView runs the same initiliation methods as the HTTP request phase:

  1. mount - The LiveView is mounted and the context is initialized
  2. handleParams - The LiveView is given a chance to handle any params passed in the URL (e.g. -GET /my-liveview?foo=bar)
  3. render - The LiveView is rendered based on the context

But instead of sending back a full HTML page, the LiveView sends back a datastructure that breaks down the HTML into -"static" and "dynamic" parts. This data structure allows future "diffs" to be sent to the client to update the DOM. -Below is a sequence diagram showing the "Websocket Join" phase:

Websocket Join

info

You may have noticed both the HTTP request phase and the Websocket Join phase run the same methods. This is -because the LiveView is initialized (mount => handleParams => render) in both phases. The HTTP phase doesn't -retain any state but the Websocket phase does keep state in memory so needs to re-run the initialization methods. -Importantly, you may also want to handle HTTP vs Websocket differently in your LiveView so calling the initialization -methods in both phases is important.

Interactive Phase

Once the Websocket has been established, the LiveView is in the "Interactive" phase. In this phase the LiveView can -respond to user events and server events.

User events (clicks, form updates/submissions, keyboard input, etc) are sent from the browser to the LiveView server -via the websocket, routed to handleEvent then render. LiveViewJS then calculates the "diffs", sends those diffs -back to the client which automatically applies them to the DOM.

Server events (from socket.sendInfo or Pub/Sub subscriptions) are automatically received in the LiveView server, -routed to handleInfo then to render Similar to user events, LiveViewJS then calculates the "diffs", sends those -diffs back to the client which automatically applies them to the DOM.

Below is a sequence diagram showing the "Interactive" phase:

Interactive

Other Processes / Phases

LiveViews have a couple of other processes that are important to understand but are automatically handled by -LiveViewJS so you don't need to worry about them.

  1. "Heartbeat" - The LiveView clientr sends a heartbeat message to the server every 30 seconds to ensure the websocket -connection is still alive
  2. "Shutdown" - The LiveView automatically cleans up and shuts down resources

Here are a couple of sequence diagrams showing the "Heartbeat" and "Shutdown" phases:

Heartbeat

Heartbeat

Shutdown

Shutdown

- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/liveviewsocket-api-context.html b/docs/docs/liveview-socket/liveviewsocket-api-context.html deleted file mode 100644 index 9eea6510..00000000 --- a/docs/docs/liveview-socket/liveviewsocket-api-context.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket API - Context | LiveViewJS - - - - -
-

LiveViewSocket API - Context

The "context" of a LiveView is the current state of the LiveView. One way to think of a LiveView is as a set of methods -that handle events, read and write the context, and render a view based on the data in the context. Obviously, -properties and methods that manipulate the context are very important to a LiveView.

Context Properties and Methods on the LiveViewSocket

Three parts of the LiveViewSocket are used to manipulate the context:

NameDescription
context (property, read-only)The current context (i.e., state) of the LiveView
assign(context:Partial<TContext>):void;Update the context (i.e., state) of the LiveView
tempAssign(context:Partial<TContext>):void;Marks any set properties as temporary and will be reset to the given value after the next render cycle. Typically used to ensure large but infrequently updated values are not kept in memory.

Details

socket.assign and socket.context are the work-horse methods for manipulating and reading the state of the LiveView. -The assign method is used to update the state of the LiveView and the context property is used to read the state of -the LiveView.

// Update the context (i.e.,  current state) of the `LiveView`
socket.assign({ foo: "bar" });
// Read the context (i.e.,  current state) of the `LiveView`
if (socket.context.foo === "baz") {
// do something
}
// or destructure data from the context
const { foo } = socket.context;

Context Type Annotations

When creating a LiveView, developers can provide a type annotation for TContext which describes the "shape" of the -context for that LiveView. This is useful for providing type safety and autocomplete for the context (in Typescript).


// You can define the "shape" of the TContext by annotating the createLiveView function
const myLiveView = createLiveView<{foo: string}>(
mount: (socket) => {
socket.assign({ foo: "bar" });
...
socket.assign({ baz: "qux" }); // type error no "baz" property in context
}
...
)

You can type the Context inline as above or you can define the context type first and then use it as a type -annotation:

// Define the MyContext interface
interface MyContext { foo: string };
// Annotate the createLiveView function with the MyContext interface
const myLiveView = createLiveView<MyContext>(...)

Context Persisted for the Life of the LiveView

The context of a LiveView is persisted on the server (in memory by default) which means any data added to the -context (via assign) will be stored until that LiveView instance is cleaned up.

Temporary Data for Context

Sometimes you want to add data to a context that is temporary that is, only added to the context for one -"render cycle". There is a method called socket.tempAssign that allows a developer to tell LiveViewJS to set a -context property to a given value after the render cycle. Typically this is used for large objects or collections that -don't change often and therefore probably don't need to be stored in memory (e.g., collection of users or messages, -etc).

// first assign a large object to the context
socket.assign({ photos: [
...// 10s, 100s, 1000s, of photos
]});
// use tempAssign to tell LiveViewJS to clear the photos array after this render cycle
socket.tempAssign({ photos: [] });
- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/liveviewsocket-api-infos.html b/docs/docs/liveview-socket/liveviewsocket-api-infos.html deleted file mode 100644 index f58983bf..00000000 --- a/docs/docs/liveview-socket/liveviewsocket-api-infos.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket API - Server Events | LiveViewJS - - - - -
-

LiveViewSocket API - Server Events

Server events are important to connect LiveViews with asynchronous processes. For example, a LiveView may need to wait -for a long database query or search service to complete before rendering the results. Or a LiveView may want to send -updates based on a webhook or action from another user.

LiveViewSocket Properties and Methods

There are two LiveViewSocket API methods that help facilitate server events: | Name | Description | |---|---| | -sendInfo(info: Info<TInfos>): void; | Send an internal event (a.k.a "Info") to the LiveView's handleInfo method | | -subscribe(topic: string): Promise<void>; | Subscribe to the given topic using pub/sub. Events published to this topic -will be delivered to handleInfo. |

sendInfo Method

socket.sendInfo enables a LiveView to send message to itself which is useful for executing actions that are -asynchronous. Messages sent via socket.sendInfo are received by the handleInfo method after the current render -lifecycle has completed. (In other words, handleInfo is called after the render call which will result in another -render after handleInfo completes.)

When creating your LiveView you can provide the typing for TInfo which describes the "shape" of the possible info -messages. e.g.

// Define the MyContext, MyEvents, and MyInfo types
type MyContext = {query: string, loading: boolean, results: string[]};
type MyEvents = {type: "search", query: string};
type MyInfo = {type: "run_search", query: string} | {type: "refresh"};

// Annotate the createLiveView function with the types
const myLiveView = createLiveView<MyContext, MyEvents, MyInfo>(
handleEvent: (event, socket) => {
...
if(event.type === "search" ) {
// update the context with loading status and empty results so
// that UI will be updated for user
socket.assign({ loading: true, results: [], query: event.query });
// send internal message to run the search process
socket.sendInfo({ type: "run_search", query: event.query })
}
}
...
handleInfo: (info, socket) => {
if(info.type === "run_search") {
const { query } = info;
// run the search
const results = searchService.run(query)
// update the context with results which will update the UI
socket.assign({ loading: false, results })
}
...
}
...
)

socket.sendInfo can take a type as a string for cases where there isn't additional information passed along with -the message.

// or send just the "type" as a string
socket.sendInfo("refresh");

subscribe Method

socket.subscribe enables a LiveView to subscribe to a topic using pub/sub. Events published to this topic will be -delivered to handleInfo. This is useful for cases where a LiveView needs to receive updates from another process or -user.

You can provide the type annotation for messages you expect to receive from a pub/sub topic as well.

- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/liveviewsocket-api-misc.html b/docs/docs/liveview-socket/liveviewsocket-api-misc.html deleted file mode 100644 index 5ad7bdba..00000000 --- a/docs/docs/liveview-socket/liveviewsocket-api-misc.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket API - Misc | LiveViewJS - - - - -
-

LiveViewSocket API - Misc

A few other methods and properties are available on the LiveViewSocket object that we haven't covered yet.

LiveViewSocket Properties and Methods

NameDescription
id (property, read-only)The (random) id of the LiveView
connected (property, read-only)Whether the websocket is connected. true if connected to a websocket, false for http request
pageTitle(newPageTitle:string):void;Updates the <title> tag of the LiveView. Requires using the live_title helper in rendering the page.
putFlash(key: string, value: string): Promise<void>;Add flash message to the socket for a given key and value.
repeat(fn: () => void, intervalMillis: number): void;Runs the given function on the given interval until this LiveView is unloaded.

id Property

The id property is a unique identifier for the LiveView. It is a random string that is generated when the LiveView is -created. It is useful for debugging and logging purposes.

connected Property

The connected property is a boolean that indicates whether the LiveView is connected to a websocket or not. If the -LiveView is connected to a websocket, then the value will be true. If the LiveView is not connected to a websocket -(i.e., executing an HTTP request), then the value will be false. This is useful for executing logic based on whether -the LiveView has completed the initial websocket connection or not. For example:

...
if (socket.connected) {
// only subscribe to the topic if the LiveView is connected to a websocket
await socket.subscribe("my_topic");
}
...

pageTitle Method

LiveViewJS provides a live_title_tag helper that enables LiveViews to update the <title> tag of the page -dynamically. This is useful for LiveViews that need to update the page title based on the current state of the LiveView. -For example, a LiveView may want to update the title to include the details of the item being viewed. The pageTitle -method works in partnership with the live_title_tag to enable dynamic page titles. live_title_tag is usually used -in the LiveViewHtmlPageTemplate template. For example:

export const htmlPageTemplate: LiveViewHtmlPageTemplate = (
liveTitleOptions: LiveTitleOptions,
csrfToken: string,
liveViewContent: LiveViewTemplate
): LiveViewTemplate => {
const pageTitle = liveTitleOptions?.title ?? "";
const pageTitlePrefix = liveTitleOptions?.prefix ?? "";
const pageTitleSuffix = liveTitleOptions?.suffix ?? "";
return html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="csrf-token" content="${csrfToken}" />
${live_title_tag(pageTitle, { prefix: pageTitlePrefix, suffix: pageTitleSuffix })}
<script defer type="text/javascript" src="/js/index.js"></script>
</head>
<body>
<p><a href="/">← Back</a><br /><br /></p>
${safe(liveViewContent)}
</body>
</html>
`;
};

Now you can update the page title from within the LiveView using the pageTitle method. For example:

...
socket.pageTitle("My New Page Title");
...
note

The pageTitle method does not update the prefix or suffix part of the live_title_tag.

putFlash Method

Flash messages are a way to display small notes or alerts to users to provide feedback on actions they are taken. -putFlash is a method that allows you to add a flash message to the LiveView. The flash message will be displayed on -the next render. The putFlash method takes two arguments: the key and the value. The key is used to identify the flash -message and the value is the message to display. putFlash usually works with a LiveViewWrapperTemplate to be used as -the root template for the LiveView. The LiveViewWrapperTemplate is used to display the flash messages. For example:

export const wrapperTemplate: LiveViewWrapperTemplate = async (
session: SessionData,
liveViewContent: LiveViewTemplate
): Promise<LiveViewTemplate> => {
const flashAdaptor = new SessionFlashAdaptor();
const infoFlash = (await flashAdaptor.popFlash(session, "info")) || "";
const errorFlash = (await flashAdaptor.popFlash(session, "error")) || "";
return html`
<main role="main" class="container">
${infoFlash !== "" ? html`<blockquote><strong>ℹ Info</strong> ${infoFlash}</blockquote>` : ""} ${errorFlash !== ""
? html`<blockquote><strong>⚠️ Error</strong> ${errorFlash}</blockquote>`
: ""} ${safe(liveViewContent)}
</main>
`;
};

repeat Method

The repeat method is a way to execute a function on a given interval. The repeat method takes two arguments: the -function to execute and the interval in milliseconds. The function will be executed on the given interval until the -LiveView is unloaded. For example:

...
if (socket.connected) {
// only start repeating if the socket is connected
socket.repeat(() => {
// send the tick event to `handleInfo` every second
socket.sendInfo({ type: "tick" });
}, 1000);
}
...
- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/liveviewsocket-api-push.html b/docs/docs/liveview-socket/liveviewsocket-api-push.html deleted file mode 100644 index 367fbfee..00000000 --- a/docs/docs/liveview-socket/liveviewsocket-api-push.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket API - Push | LiveViewJS - - - - -
-

LiveViewSocket API - Push

There are various methods for "pushing" data from the server to the client outside of render and updating the URL of -the LiveView.

LiveViewSocket Properties and Methods

NameDescription
pushEvent(pushEvent: AnyLivePushEvent): void;Pushes and event (possibly with data) from the server to the client. Requires either a window.addEventListener defined for that event or a client Hook to be defined and to be listening for the event via this.handleEvent callback.
pushPatch(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;Updates the LiveView's browser URL with the given path and query parameters.
pushRedirect(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;Shutdowns the current LiveView and loads another LiveView in its place without reloading the whole page (i.e., making a full HTTP request). Can be used to remount the current LiveView if need be. Use pushPatch to update the current LiveView without unloading and remounting.

Pushing Events

An event is any JSON object with a type: string property and optionally any other key/value pairs. e.g., :

{
type: "my-event",
foo: "bar"
}

Events can be pushed to the client using socket.pushEvent:

socket.pushEvent({
type: "my-event",
foo: "bar",
});
note

Event names are prefixed with phx: so an event with the type my-event will be sent as phx:my-event.

Listening for Events on the Client

Events can be listened for in two ways:

  1. Using window.addEventListener:
window.addEventListener("phx:my-event", (event) => {
console.log(event.detail.foo); // "bar"
});
  1. Using a client Hook:
this.handleEvent("my-event", (event) => {
console.log(event.foo); // "bar"
});
We haven't discussed Hooks yet but they are not in any way like a React Hook. They are a way to define a

client-side component that can be used in your LiveView. We'll cover them in more detail in a later section.

For now, just know that you can define a client-side component that can listen for events and do something with them.

URL-based Pushes

The LiveViewSocket has two methods for updating the URL of the LiveView:

  • pushPatch - Updates the LiveView's browser URL with the given path and query parameters.
  • pushRedirect - Shutdowns the current LiveView and loads another LiveView in its place without reloading the whole -page (i.e., making a full HTTP request). Can be used to remount the current LiveView if need be.

pushPatch Example

// Update the URL to /foo?bar=baz
socket.pushPatch("/foo", new URLSearchParams({ bar: "baz" }));

pushPatch will cause the handleParams method to be invoked which can be used to update the LiveView's state based on -the new URL parameters.

pushRedirect Example

// Shutdown the current LiveView and load a new LiveView at /foo?bar=baz
socket.pushRedirect("/foo", new URLSearchParams({ bar: "baz" }));

pushRedirect will cause the current LiveView to be shutdown and a new LiveView to be loaded at the given path and -query parameters without reloading the whole page (i.e., making a full HTTP request).

- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/liveviewsocket-api-uploads.html b/docs/docs/liveview-socket/liveviewsocket-api-uploads.html deleted file mode 100644 index 4381bcb7..00000000 --- a/docs/docs/liveview-socket/liveviewsocket-api-uploads.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket API - Uploads | LiveViewJS - - - - -
-

LiveViewSocket API - Uploads

A common use case for a web application is to allow users to upload files. The following methods on the LiveViewSocket -enable you to upload files to your server.

LiveViewSocket Properties and Methods

NameDescription
allowUpload(name: string, options?: UploadConfigOptions): Promise<void>;Allows file uploads for the given LiveView and configures the upload options (filetypes, size, etc).
cancelUpload(configName: string, ref: string): Promise<void>;Cancels the file upload for a given UploadConfig by config name and file ref.
consumeUploadedEntries<T>(configName: string,fn: (meta: ConsumeUploadedEntriesMeta, entry: UploadEntry) => Promise<T>):Promise<T[]>;Consume the uploaded files for a given UploadConfig (by name). This should only be called after the form's "save" event has occurred which guarantees all the files for the upload have been fully uploaded.
uploadedEntries(configName: string): Promise<{completed: UploadEntry[];inProgress: UploadEntry[];}>;Returns two sets of files that are being uploaded, those completed and those inProgress for a given UploadConfig (by name). Unlike consumeUploadedEntries, this does not require the form's "save" event to have occurred and will not throw if any of the entries are not fully uploaded.

allowUpload Method

allowUpload is used to configure the file upload options for a given LiveView. This method should be called in the -mount method of your LiveView. The options parameter is optional and if not provided, the default options will be -used. allowUpload requires a name parameter which is used to identify the upload config elsewhere in your code. Here -is an example of configuring the upload options for a LiveView using allowUpload:

...
mount: (socket) => {
...
// configure the upload constraints
socket.allowUpload("photos", {
accept: [".png", ".jpg", ".jpeg", ".gif"], // only allow images
maxEntries: 3, // only allow 3 files to be uploaded
maxFileSize: 10 * 1024 * 1024, // 10MB
});
...
}
...

Now that you've configured the upload options, you can use those options to render a live_file_input tag that allows -the user to upload files. Here is an example of a live_file_input tag that uses the photos upload config as part of -your render template:

<!-- use the "photos" upload config -->
<div>${live_file_input(uploads.photos)}</div>

cancelUpload Method

cancelUpload is a way to remove a file from the set of entries that either are ready to be uploaded or have already -been uploaded (but not fully consumed yet). Typically, you would call this method in response to a user action such as -clicking a "remove" button next to a file. Here is an example of calling cancelUpload in response to a user action:

handleEvent: (socket, event) => {
...
switch (event.type) {
...
case "cancel":
const { config_name, ref } = event;
// remove the uploaded entry from the upload config
socket.cancelUpload(config_name, ref);
break;
...
}
...
}
...
render: (context, meta) {
...
<a phx-click="cancel" phx-value-config_name="photos" phx-value-ref="${entry.ref}">🗑</a>
...
}
...

consumeUploadedEntries Method

consumeUploadedEntries is a way to fully process the uploaded files for a given UploadConfig (by name). This -should only be called after the form's "save" event has occurred which guarantees all the files for the upload have been -fully uploaded. Here is an example of calling consumeUploadedEntries after the form's "save" event has occurred:

...
handleEvent: (socket, event) => {
...
switch (event.type) {
...
case "save":
...
// consume the uploaded entries for the "photos" upload config
await socket.consumeUploadedEntries("photos", async (meta, entry) => {
// we could create thumbnails, scan for viruses, etc.
// but for now move the data from the temp file (meta.path) to a public directory
meta.fileSystem.createOrAppendFile(`./public/${filename(entry)}`, meta.path);
});
...
break;
...
}
...
}
...

uploadedEntries Method

uploadedEntries returns the set of files that are either in progress of being uploaded or have already been uploaded -(but not fully consumed yet). Unlike consumeUploadedEntries this can be called before the form's save event has -occured.

...
// get the complete and in progress entries for the "photos" upload config
const { completed, inProgress } = await socket.uploadedEntries("photos");
// do something with the entries
...

More details

More details on file uploads can be found in the File Uploads section of the docs.

- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/liveviewsocket-api.html b/docs/docs/liveview-socket/liveviewsocket-api.html deleted file mode 100644 index e6f2bc1d..00000000 --- a/docs/docs/liveview-socket/liveviewsocket-api.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -LiveViewSocket API | LiveViewJS - - - - -
-

LiveViewSocket API

The LiveViewSocket API is the second most important API (behind the LiveView API itself). As you've seen, the -LiveViewSocket is passed into (as the socket param) just about all of the LiveView API methods (excluding render). -So far we've mainly used it to update the context of the LiveView. But there is a lot more to LiveViewSocket and in -this section we'll review the full API.

LiveViewSocket Properties and Methods

NameDescription
id (property, read-only)The (random) id of the LiveView
connected (property, read-only)Whether the websocket is connected. true if connected to a websocket, false for http request
context (property, read-only)The current context (i.e., state) of the LiveView
assign(context:Partial<TContext>):void;Update the context (i.e., state) of the LiveView
tempAssign(context:Partial<TContext>):void;Marks any set properties as temporary and will be reset to the given value after the next render cycle. Typically used to ensure large but infrequently updated values are not kept in memory.
pageTitle(newPageTitle:string):void;Updates the <title> tag of the LiveView. Requires using the live_title helper in rendering the page.
putFlash(key: string, value: string): Promise<void>;Add flash message to the socket for a given key and value.
pushEvent(pushEvent: AnyLivePushEvent): void;Pushes an event from the server to the client. Requires a client Hook to be defined and to be listening for the event via this.handleEvent callback.
pushPatch(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;Updates the LiveView's browser URL with the given path and query parameters.
pushRedirect(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;Shutdowns the current LiveView and loads another LiveView in its place without reloading the whole page (i.e., making a full HTTP request). Can be used to remount the current LiveView if need be. Use pushPatch to update the current LiveView without unloading and remounting.
sendInfo(info: Info<TInfos>): void;Send an internal event (a.k.a "Info") to the LiveView's handleInfo method
subscribe(topic: string): Promise<void>;Subscribe to the given topic using pub/sub. Events published to this topic will be delivered to handleInfo.
repeat(fn: () => void, intervalMillis: number): void;Runs the given function on the given interval until this LiveView is unloaded.
allowUpload(name: string, options?: UploadConfigOptions): Promise<void>;Allows file uploads for the given LiveView and configures the upload options (filetypes, size, etc).
cancelUpload(configName: string, ref: string): Promise<void>;Cancels the file upload for a given UploadConfig by config name and file ref.
consumeUploadedEntries<T>(configName: string,fn: (meta: ConsumeUploadedEntriesMeta, entry: UploadEntry) => Promise<T>):Promise<T[]>;Consume the uploaded files for a given UploadConfig (by name). This should only be called after the form's "save" event has occurred which guarantees all the files for the upload have been fully uploaded.
uploadedEntries(configName: string): Promise<{completed: UploadEntry[];inProgress: UploadEntry[];}>;Returns two sets of files that are being uploaded, those completed and those inProgress for a given UploadConfig (by name). Unlike consumeUploadedEntries, this does not require the form's "save" event to have occurred and will not throw if any of the entries are not fully uploaded.

We'll look at related parts of the LiveViewSocket API over the next few sections:

  • Reading and Writing the Context
  • Pushing Updates to the Client
  • Server Events / Pub/Sub
  • File Uploads
  • Everything Else
- - - - \ No newline at end of file diff --git a/docs/docs/liveview-socket/raw-liveviewsocket-api.html b/docs/docs/liveview-socket/raw-liveviewsocket-api.html deleted file mode 100644 index 055adc47..00000000 --- a/docs/docs/liveview-socket/raw-liveviewsocket-api.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -Raw `LiveViewSocket` API | LiveViewJS - - - - -
-

Raw LiveViewSocket API

For your reference, below is the raw Typescript for the LiveViewSocket interface (copied from -here):

packages/core/src/server/socket/liveSocket.ts
/**
* Main interface to update state, interact, message, and otherwise
* manage the lifecycle of a `LiveView`.
*
* The `LiveView` API (i.e., `mount`, `handleParams`, `handleInfo`, `handleEvent`)
* are all passed `LiveViewSocket` which provide access to the current `LiveView`
* context (via `context`) as well as various methods update the `LiveView` including
* `assign` which updates the `LiveView`'s context (i.e., state).
*/
export interface LiveViewSocket<TContext extends LiveContext = AnyLiveContext, TInfos extends LiveInfo = AnyLiveInfo> {
/**
* The id of the `LiveView`
*/
readonly id: string;
/**
* Whether the websocket is connected.
* true if connected to a websocket, false for http request
*/
readonly connected: boolean;
/**
* The current context (i.e., state) of the `LiveView`
*/
readonly context: TContext;
/**
* `assign` is used to update the context (i.e., state) of the `LiveComponent`
* @param context a `Partial` of the LiveView's context to update
*/
assign(context: Partial<TContext>): void;
/**
* Marks any set properties as temporary and will be reset to the given
* value after the next render cycle. Typically used to ensure large but
* infrequently updated values are not kept in memory.
*
* @param context a partial of the context that should be temporary and the value to reset it to
*/
tempAssign(context: Partial<TContext>): void;
/**
* Updates the `<title>` tag of the `LiveView` page. Requires using the
* `live_title` helper in rendering the page.
*
* @param newPageTitle the new text value of the page - note the prefix and suffix will not be changed
*/
pageTitle(newPageTitle: string): void;
/**
* Pushes an event from the server to the client. Requires a
* client `Hook` to be defined and to be listening for the event
* via `this.handleEvent` callback.
*
* @param pushEvent the event to push to the client
*/
pushEvent(pushEvent: AnyLivePushEvent): void;
/**
* Updates the LiveView's browser URL with the given path and query parameters.
*
* @param path the path whose query params are being updated
* @param params the query params to update the path with
* @param replaceHistory whether to replace the current history entry or push a new one (defaults to false)
*/
pushPatch(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;
/**
* Shutdowns the current `LiveView`and loads another `LiveView`in its place
* without reloading the whole page (i.e., making a full HTTP request). Can be
* used to remount the current `LiveView`if need be. Use `pushPatch` to update the
* current `LiveView`without unloading and remounting.
*
* @param path the path whose query params are being updated
* @param params the query params to update the path with
* @param replaceHistory whether to replace the current history entry or push a new one (defaults to false)
*/
pushRedirect(path: string, params?: URLSearchParams, replaceHistory?: boolean): Promise<void>;
/**
* Add flash to the socket for a given key and value.
* @param key the key to add the flash to
* @param value the flash value
*/
putFlash(key: string, value: string): Promise<void>;
/**
* Runs the given function on the given interval until this `LiveView` is
* unloaded.
*
* @param fn the function to run on the interval
* @param intervalMillis the interval to run the function on in milliseconds
*/
repeat(fn: () => void, intervalMillis: number): void;
/**
* Send an internal event (a.k.a "Info") to the LiveView's `handleInfo` method
*
* @param event the event to send to `handleInfo`
*/
sendInfo(info: Info<TInfos>): void;
/**
* Subscribe to the given topic using pub/sub. Events published to this topic
* will be delivered to `handleInfo`.
*
* @param topic the topic to subscribe this `LiveView`to
*/
subscribe(topic: string): Promise<void>;

/**
* Allows file uploads for the given `LiveView`and configures the upload
* options (filetypes, size, etc).
* @param name the name of the upload
* @param options the options for the upload (optional)
*/
allowUpload(name: string, options?: UploadConfigOptions): Promise<void>;

/**
* Cancels the file upload for a given UploadConfig by config name and file ref.
* @param name the name of the upload from which to cancel
* @param ref the ref of the upload entry to cancel
*/
cancelUpload(configName: string, ref: string): Promise<void>;

/**
* Consume the uploaded files for a given UploadConfig (by name). This
* should only be called after the form's "save" event has occurred which
* guarantees all the files for the upload have been fully uploaded.
* @param name the name of the upload from which to consume
* @param fn the callback to run for each entry
* @returns an array of promises based on the return type of the callback function
* @throws if any of the entries are not fully uploaded (i.e., completed)
*/
consumeUploadedEntries<T>(
configName: string,
fn: (meta: ConsumeUploadedEntriesMeta, entry: UploadEntry) => Promise<T>
): Promise<T[]>;

/**
* Returns two sets of files that are being uploaded, those `completed` and
* those `inProgress` for a given UploadConfig (by name). Unlike `consumeUploadedEntries`,
* this does not require the form's "save" event to have occurred and will not
* throw if any of the entries are not fully uploaded.
* @param name the name of the upload from which to get the entries
* @returns an object with `completed` and `inProgress` entries
*/
uploadedEntries(configName: string): Promise<{
completed: UploadEntry[];
inProgress: UploadEntry[];
}>;
}
- - - - \ No newline at end of file diff --git a/docs/docs/misc/debugging-wire.html b/docs/docs/misc/debugging-wire.html deleted file mode 100644 index dc8465ae..00000000 --- a/docs/docs/misc/debugging-wire.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Debugging LiveViews | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/misc/flash.html b/docs/docs/misc/flash.html deleted file mode 100644 index 578ec0e5..00000000 --- a/docs/docs/misc/flash.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Flash Messages | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/misc/livecomponents.html b/docs/docs/misc/livecomponents.html deleted file mode 100644 index b74f2879..00000000 --- a/docs/docs/misc/livecomponents.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - -Live Components | LiveViewJS - - - - -
-

Live Components

We've mostly been talking about LiveViews, but LiveViewJS also supports LiveComponents. Live Components are a way -to create self-contained, stateful and stateless components that are reusable across multiple LiveViews. Live Components -are a great way to break up your LiveView into smaller, reusable, more manageable pieces.

LiveComponent API

LiveComponents have a much simpler API than LiveViews. They have the following methods all of which are optional except -for render:

  • mount
  • update
  • handleEvent
  • render (required)

Stateful vs Stateless

LiveComponents can be stateful or stateless. Stateful components are identified by passing an id property into the -meta.live_component function in the LiveView. Stateless LiveComponents do not have an id property. Stateful -components are also the only type of LiveComponent that can receive events (and therefore have a handleEvent method).

Lifecycle Differences

Both types of LiveComponents have the same lifecycle methods. Both types initially follow the same execution flow when -they are first loaded:

mount => update => render

With Stateless LiveComponents, the execution flow above is the same for every render cycle. For Stateful LiveComponents, -after the first render cycle, the execution flow is:

update => render

Stateful LiveComponent handleEvent

Targeting a LiveComponent requires the addition of a phx-target attribute to a the rendered HTML element. Inside of a -render a LiveComponent can use the meta.myself property in the phx-target attribute to target itself. For example:

<button phx-click="my_event" phx-target="${meta.myself}">Click Me</button>

Alternatively, you can target another LiveComponent by passing the DOM id or class selector into the phx-target -attribute. For example:

<button phx-click="my_event" phx-target="#comp_3">Click Me</button>

In either case, the handleEvent method will be called with the my_event event prompting a re-render of the -LiveComponent.

Adding a LiveComponent to a LiveView

To add a LiveComponent to a LiveView, you use the LiveViewMeta live_component function. The live_component -function takes a LiveComponent along with a JSON object with an optional id property. If the id property is present, -the LiveComponent will be stateful. If the id property is not present, the LiveComponent will be stateless. For -example:

...
render: (context, meta) => {
return html`
<div>
${meta.live_component(MyStatefulComponent, {id: "comp_1", bar: "baz"})}
${meta.live_component(MyStatefulComponent, {id: "comp_2"})}
${meta.live_component(MyStatelessComponent, {foo: "bar"})}
</div>
`
}
...

LiveComponentSocket API

Similar to LiveViews, LiveComponents have a LiveComponentSocket API that is the utility belt for LiveComponents. Below -is the API for LiveComponentSocket:

/**
* Represents the `LiveComponent`'s websocket connectedness along with current
* state of the component. Also provides a method for sending messages
* internally to the parent `LiveView`.
*/
export interface LiveComponentSocket<
TContext extends LiveContext = AnyLiveContext,
TInfo extends LiveInfo = AnyLiveInfo
> {
/**
* The id of the parent `LiveView`
*/
id: string;
/**
* Whether the websocket is connected (i.e., http request or joined via websocket)
* true if connected to a websocket, false for http request
*/
connected: boolean;
/**
* Read-only, current state of the `LiveComponent`
*/
context: TContext;
/**
* helper method to send messages to the parent `LiveView` via the `handleInfo`
*/
sendParentInfo(info: Info<TInfo>): void;
/**
* `assign` is used to update the `Context` (i.e., state) of the `LiveComponent`
*/
assign(context: Partial<TContext>): void;
/**
* helper method to send events to Hooks on the parent `LiveView`
*/
pushEvent(pushEvent: AnyLivePushEvent): void;
}
- - - - \ No newline at end of file diff --git a/docs/docs/misc/root-and-page-renderers.html b/docs/docs/misc/root-and-page-renderers.html deleted file mode 100644 index fee31ca0..00000000 --- a/docs/docs/misc/root-and-page-renderers.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -Root and Page Renderers | LiveViewJS - - - - - - - - - \ No newline at end of file diff --git a/docs/docs/misc/security-topics.html b/docs/docs/misc/security-topics.html deleted file mode 100644 index d31d42a0..00000000 --- a/docs/docs/misc/security-topics.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - -Security Topics | LiveViewJS - - - - -
-

Security Topics

Here are some security topics to consider when using LiveViewJS.

Authenticity Tokens / CSRF Protection

We use CSRF tokens (a.k.a. Authenticity Tokens) to protect against Cross-Site Request Forgery (CSRF) attacks. CSRF -attacks are a type of malicious exploit where unauthorized commands are performed on behalf of an authenticated user.

Websocket Join Authenticity

Every LiveView page load embeds a CSRF token in the page header in a meta tag named csrf-token. This token is -created by LiveViewJS when the HTML page is initially rendered. The LiveViewJS client-side code automatically -pulls in that token and sends it to the server as part of the websocket join phase where it is used to verify that the -request is legitimate.

LiveViewJS Forms Authenticity

LiveViewJS expects forms to have an input named _csrf_token. This input field is automatically added to forms by the -form_for helper and populated by the csrf token passed in during join. If you don't use form_for you can add a -hidden input with the name _csrf_token and populate it yourself. When the form is submitted, the _csrf_token input -field is sent to the server along with the form data. The LiveViewJS server verifies that the submitted CSRF token -matches the CSRF token from the page header. If they do not match, the request is rejected. If the CSRF token is -missing, the server prints out a warning but allows the form submission to continue.

Session Data

Part of the webserver integration is being able to pass any session data from the HTTP session to the LiveView websocket -session. LiveViewJS allows each webserver integration to implement a getSessionData method that returns a JSON -object containing the session data. LiveViewJS then uses the webserver integration's serializer/deserializer (a.k.a. -SerDe) to serialize this data to be passed to the server as part of the websocket join. The LiveViewJS server then -deserializes the session data and makes it available to the LiveView via the session property in mount.

Default SerDe uses JWT

The default SerDe implementation uses JWT to serialize and deserialize the session data. This means that the session -data is signed (which prevents tampering) but not encrypted. If you want to encrypt the session data, you will have to -implement your own SerDe.

Please Ask Questions 🎁

If there is something you are concerned about regarding security and LiveViewJS, please add an issue to the -LiveViewJS GitHub repo. We will do our best to answer your questions and -address any concerns you may have.

- - - - \ No newline at end of file diff --git a/docs/docs/misc/statics-and-dynamics.html b/docs/docs/misc/statics-and-dynamics.html deleted file mode 100644 index 31f43314..00000000 --- a/docs/docs/misc/statics-and-dynamics.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - -Statics and Dynamics | LiveViewJS - - - - -
-

Statics and Dynamics

It is helpful to understand why and how LiveViewJS takes a HTML template and breaks it into static and dynamic -parts.

Template Example

Let's say we have the following template being returned in a LiveView's render function:

...
render: (context, meta) => {
const {title, body} = context;
return html`
<div>
<h1>${title}</h1>
<p>${body}</p>
</div>
`
}
...
info

The html tag is a "tagged template literal" function which allows LiveViewJS to parse the template literal -into a tree of static and dynamic parts. For more information on tagged template literals, see -MDN.

Parts of the Template

The template above is pretty simple but easy to see how it can break into static parts and dynamic parts. There are two -dynamic parts of the template: ${context.title} and ${context.body}. The rest of the template is static. The parts -break down into something like this:

// array of static parts
const statics = [
"
<div>
<h1>",
"</h1>
<p>",
"</p>
</div>
"
];

// array of dynamic parts
const dynamics = [
title,
body
];

Zip Together

You can see that once we resolve the values for title and body we can "zip" these two arrays together to create the -final HTML string. This is exactly what LiveViewJS does when it renders a HTML LiveView.

Send Both Statics and Dynamics to Client

In the case of the websocket, LiveViewJS initially sends both the statics and dynamics to the client. The client -then uses the statics and dynamics to render the HTML. The client also stores the statics in memory so that it can use -them to re-render the HTML when the dynamics change.

Only Update the Dynamics

When updates occur on the server and the LiveView is rerendered, we don't need to send the statics again. We only need -to send the dynamics and furthermore, we only need to send the dynamics that have changed. The client then uses the -stored statics and the new dynamics to re-render the HTML.

Super Fast 🏎

This sending only the value of a dynamic part of the LiveView that changed is extremely efficient and allows -LiveViewJS to be super fast. It also allows LiveViewJS to be very lightweight. The client only needs to store -the statics in memory and the server only needs to send the dynamics that have changed.

- - - - \ No newline at end of file diff --git a/docs/docs/overview/feedback.html b/docs/docs/overview/feedback.html deleted file mode 100644 index 8f792663..00000000 --- a/docs/docs/overview/feedback.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -Getting Involved | LiveViewJS - - - - -
-

Getting Involved

Feedback is a 🎁

Like all software, this is a work in progress. If you have any feedback, please let us know by opening an issue on the -GitHub repository.

Contributing is ❤️

We welcome questions, comments, documentation, and code contributions. If you'd like to contribute, please feel free to -open an issue or a pull request. We'll do our best to respond quickly and get it merged!

Sponsorship is ⛽️

If you'd like to support the project, please consider sponsoring us on GitHub. -We'll use the funds to pay for coffee, hosting, domain names, therapy, and other expenses related to the project.

- - - - \ No newline at end of file diff --git a/docs/docs/overview/gratitude.html b/docs/docs/overview/gratitude.html deleted file mode 100644 index 42265844..00000000 --- a/docs/docs/overview/gratitude.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - -Gratitude | LiveViewJS - - - - -
-

Gratitude

🙌 Thanks to the Phoenix LiveView team for the genius conceptualization and implementation of LiveViews and all the code -we can reuse on the client side.

🙌 Thanks to @ogrodnek for the early support, feedback, and the idea to reuse the Phoenix -client code instead of reinventing!

🙌 Thanks to @blimmer for the awesome feedback, documentation suggestions, and support!

- - - - \ No newline at end of file diff --git a/docs/docs/overview/introduction.html b/docs/docs/overview/introduction.html deleted file mode 100644 index efa676d3..00000000 --- a/docs/docs/overview/introduction.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - -Introduction | LiveViewJS - - - - -
-

Introduction

LiveViewJS is an open-source framework for "LiveView"-based, full-stack applications in NodeJS and Deno.

The LiveView approach allows developers to build applications with rich user experiences like React, Vue, etc, but with -far less code and complexity and far more speed and efficiency.

What is a LiveView?

A LiveView is a server-rendered HTML page that connects to the server via a persistent web socket. The web socket allows -the client to send user events to the server and the server to send diffs in response. LiveViewJS is a LiveView -framework that handles the complex part of this (handling websockets, diffing changes, applying DOM updates, etc.) so that -you can focus on building your application.

Here's the typical "counter" example written as a LiveView in LiveViewJS:

import { createLiveView, html } from "liveviewjs";

/**
* A basic counter that increments and decrements a number.
*/
export const counterLiveView = createLiveView<
{ count: number }, // Define LiveView Context (a.k.a state)
{ type: "increment" } | { type: "decrement" } // Define LiveView Events
>({
mount: (socket) => {
// init state, set count to 0
socket.assign({ count: 0 });
},
handleEvent: (event, socket) => {
// handle increment and decrement events
const { count } = socket.context;
switch (event.type) {
case "increment":
socket.assign({ count: count + 1 });
break;
case "decrement":
socket.assign({ count: count - 1 });
break;
}
},
render: (context) => {
// render the view based on the state
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});

And here is what that LiveView looks like in a browser: -LiveView Counter Example Screen Recording

Yes, it "looks" like a React/Vue/Svelt UI but the main differences are:

  • This page was first rendered as plain HTML (not a bundle of JS)
  • The client is automatically connected to a server via a websocket
  • The click events are automatically shipped to the server
  • The server then runs the business logic to update the state
  • Using the new state, the server then renders a new view and calculates any diffs
  • Those diffs are shipped back to the client, where they are automatically applied

Pretty cool eh? We think so too! While this counter LiveView isn't particularly useful, it gives you a quick intro to how -LiveViews work and what they look like both as code and in the browser. We've got a lot more to show you about -LiveViewJS including: built-in real-time / multi-player support, built-in form validation with changesets, built-in -file uploads with image previews and drag and drop support, and more!

But first, a bit more about LiveViews...

LiveView is already proven technology

Phoenix LiveView is already extremely popular in the -Elixir community and has been used to build production applications for years. It powers delightful user experiences and is battle-tested in terms of performance, reliability, and developer productivity.

Here is a quote from the author and inventor of LiveView, Chris McCord from -"how we got to liveview":

LiveView strips away layers of abstraction, because it solves both the client and server in a single abstraction. HTTP -almost entirely falls away. No more REST. No more JSON. No GraphQL APIs, controllers, serializers, or resolvers. You -just write HTML templates, and a stateful process synchronizes it with the browser, updating it only when needed.

Advantages

  • Faster way to build rich, full-stack application - LiveView abstracts away the complexity of client/server -communication, state management, and synchronization to make it simple and fast to build rich, server-connected user -experiences. See LiveView Paradigm for more details.
  • Real-time and multi-player built-in - Broadcasting updates to single or multiple clients is natively supported in -LiveViewJS. Building features like chat, presence, and real-time dashboards are all supported by LiveView's Pub/Sub. -We ship with support for Redis (NodeJS) and BroadcastChannels (Deno).
  • Extremely fast "first paint" - No massive JS bundle downloads, virtual DOM, "hydration", data-fetching, or the -like. LiveViews are first rendered statically as part of a normal HTTP response. This means "first-paint" is extremely -fast.
  • Extremely fast user-initiated updates - LiveView automatically maintains a persistent socket connection between -client and server. User events are sent to the server and optimized diffs are sent back to the client. All this -happens extremely fast and transparently to user and developer.
  • No need to build REST or GraphQL APIs - Full-stack applications usually have front-end and back-end code bases -adding complexity, effort, and context switching. The LiveView paradigm merges front-end and back-end -into a single abstraction layer which greatly increases productivity.
  • Robust, battle-tested browser libraries - LiveViewJS uses the exact same browser libraries as Phoenix -LiveView which has 10s of thousands of production applications serving millions of users. This isn't some new, -unproven technology.
  • No client/server state synchronization challenges - State synchronization between client and server is super -complex for traditional SPAs. LiveViews do not have to worry about the challenges inherent in state synchronization -because the server is always the source of truth and client updates are pushed to the client.
  • No client-side routing - No need to use a library like React Router or Vue Router. LiveViews route like multi-page -applications which is handled automatically by the browser and server. (Another major complication rendered -unnecessary.)
  • No component libraries (or Storybook) required - Traditional SPAs require teams to adopt, build or buy and then -maintain, and customize a component library and use something like Storybook. LiveView -simplifies this greatly with server-side HTML templates.

Disadvantages

  • Not a drop-in replacement for traditional SPAs - LiveView is a new paradigm and is not a drop-in replacement for -traditional SPA frameworks like React or Vue. It is a new way of building full-stack applications.
  • Not a great solution for pure static sites - Static sites that do not have user events or don't update often are -not a great fit for LiveView.

How is this different than Phoenix LiveView?

The Phoenix project's backend is written in Elixir and runs on the ErlangVM. LiveViewJS is a protocol compliant, -implementation of Phoenix LiveView but written in Typescript and runs on NodeJS and Deno. We want to bring the magic -and productivity of LiveView to the NodeJS and Deno ecosystems and are obviously huge fans of Phoenix LiveView and the -team that invented it. We believe in it so much that we think more developers should have access to the programming -paradigm it enables.

Reach more developers

Unfortunately, Elixir only represents -about 2% -of the programming language market share. We believe that LiveViewJS will help bring the productivity and magic of -LiveView to the -65% of developers -that use Javascript (and Typescript).

- - - - \ No newline at end of file diff --git a/docs/docs/overview/paradigm.html b/docs/docs/overview/paradigm.html deleted file mode 100644 index 1c253b81..00000000 --- a/docs/docs/overview/paradigm.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - -LiveView Paradigm | LiveViewJS - - - - -
-

LiveView Paradigm

The LiveView model is simple. The server renders an HTML page when a user makes the initial HTTP request. That page -then connects to the server via a persistent web socket. From there, user-initiated events (clicks, form input, key -events, focus/blur events, etc) are sent over the web socket to the server in very small packets. When the server -receives the events, it runs the business logic for that LiveView, calculates the new rendered HTML, and then sends only -the diffs to the client. The client automatically updates the page with the diffs. The server can also send diffs back -to the client based on events on the server or received from other clients (think chat, or other pub/sub scenarios).

info

LiveViewJS solves the complex parts of LiveViews such as connecting and managing web sockets, diffing and -patching the UI, routing events, real-time/multiplayer, file uploads, and more.

How is this different from SPAs?

SPA-frameworks (React, Vue, Svelt, etc) only manage state and rendering on the client. You need a completely different -backend to handle business logic and persistence, typically a REST or GRAPHQL API (and related auth). This means you -need to write two code bases, one for the front-end and one for the back-end and then integrate them. LiveViewJS is -a single code base that handles both the front-end and back-end while enabling the same rich user experiences that a -SPA enables. With LiveViewJS you write your business logic and persistence code in the same place as your front-end -code. This greatly simplifies the development process, reduces the number of moving parts, and increases development velocity.

It's worth re-reading Chris McCord's quote in the Introduction, or even better, read these docs and run the -examples! 😀 You'll see how easy it is to build rich, interactive, and responsive user experiences with LiveViewJS -and start to understand how much of an improvement and paradigm shift it is.

- - - - \ No newline at end of file diff --git a/docs/docs/overview/runtimes.html b/docs/docs/overview/runtimes.html deleted file mode 100644 index e74a9e90..00000000 --- a/docs/docs/overview/runtimes.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - -Packages & Runtimes | LiveViewJS - - - - -
-

Packages & Runtimes

LiveViewJS is written in Typescript and runs on both NodeJS and Deno. -We've abstracted away APIs specific to each runtime so that you can write your code once and run it on both -runtimes, assuming you don't use any runtime-specific APIs. That said, most developers are targeting one or the other, and -we've made it easy to use LiveViewJS with either runtime.

NodeJS and Deno Differences

As mentioned, we've worked hard to abstract away the differences between NodeJS and Deno so that you can write your code -once and run it on both runtimes. The only major difference between the two is with the following:

  • Pub/Sub - NodeJS has a Redis implementation for multi-process pub/sub while Deno uses -BroadcastChannel for multi-process pub/sub. Out of the box, -both NodeJS and Deno use the same single process EventEmitter-based implementation.
  • HTTP Server Libraries - With NodeJS, we ship with support for Express. Deno's HTTP server -framework is called Oak, and we ship with support for that. Theoretically, any HTTP -server framework that supports websockets should work with LiveViewJS. We've only tested with Express and Oak, but we'd -love to hear about your experiences with other frameworks and help support your framework of choice.

Other Webservers?

We've built out support for Express and Oak, but theoretically, any javascript HTTP webserver with support for websockets -should work with LiveViewJS. See webserver integration for more -details on integrating with your webserver of choice.

What about Bun?

LiveViewJS also runs on Bun. We basically ran the NodeJS (express) version on Bun, and it works great! -Let us know if you run into any issues running LiveViewJS on Bun.

LiveViewJS Packages

LiveViewJS is broken up into the following packages:

  • liveviewjs Node and Deno - The core -package that contains the LiveViewJS core server code. This package is runtime agnostic and can be used with -either NodeJS or Deno.
  • @liveviewjs/examples Node or -Deno - The package contains all the example LiveViews that -can be run on either NodeJS or Deno. This package is runtime agnostic and can be used with either NodeJS or Deno.
  • @liveviewjs/express Node - The Express package that contains -the Express server integration code. This package is used to integrate LiveViewJS with Express (NodeJS).
  • https://deno.land/x/liveviewjs@VERSION/packages/deno/mod.ts - The Deno package that contains the Oak server -integration code. This package is used to integrate LiveViewJS with Oak (Deno).
- - - - \ No newline at end of file diff --git a/docs/docs/quick-starts/deno-build-first-liveview.html b/docs/docs/quick-starts/deno-build-first-liveview.html deleted file mode 100644 index 5e869f2e..00000000 --- a/docs/docs/quick-starts/deno-build-first-liveview.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - -Deno - Build a LiveView | LiveViewJS - - - - -
-

Deno - Build a LiveView

Since you've already downloaded the LiveViewJS repo, it should be easy to create a new -LiveView and add it to your webserver. Let's get started!

Create a new LiveView in Deno

Since we are using Deno to serve our LiveViews, we'll create a new LiveView in the packages/deno directory.

Use your favorite editor to create a new file packages/deno/src/example/liveview/hello.ts and add the following code -and hit save:

import { createLiveView, html } from "liveviewjs";

export const helloLiveView = createLiveView({
render: () => html`Hello World!`,
});

Congratulations! You've just created your first LiveView! It doesn't do much yet but let's get it running in the -browser.

Setup a new Route

Let's add a route to this LiveView to see it in our browser. Edit packages/deno/src/example/index.ts and make -the following highlighted changes:

packages/deno/src/example/index.ts
...
import { pageRenderer, rootRenderer } from "./liveViewRenderers.ts";
import { helloLiveView } from "./liveviews/hello.ts";

// map request paths to LiveViews
const lvRouter: LiveViewRouter = {
"/hello": helloLiveView,
"/autocomplete": autocompleteLiveView,
...

Great! We've now setup our new LiveView to be served at the /hello path. Let's start the server and see it in action.

Start the Oak Server

Start up the Oak server in Deno:

# start oak server
deno run --allow-net --allow-read --allow-write --allow-env --import-map=import_map.json src/example/index.ts

See the LiveView in Action

Point your browser to http://localhost:9001/hello and you should see something like the -following: LiveViewJS Hello World Screenshot

Next Steps

Ok we got our first LiveView running but it isn't very interactive. Let's make it more interesting by adding a button -that toggles between using text and emojis to say hello. Update the hello.ts file to the following:

packages/deno/src/example/liveview/hello.ts
import { createLiveView, html } from "liveviewjs";

export const helloLiveView = createLiveView({
mount: (socket) => {
socket.assign({ useEmoji: false });
},
handleEvent(event, socket) {
socket.assign({ useEmoji: !socket.context.useEmoji });
},
render: (context) => {
const msg = context.useEmoji ? "👋 🌎" : "Hello World";
return html`
${msg}
<br />
<button phx-click="toggle">Toggle Message</button>
`;
},
});

Stop the Deno server and run the same command again to start the server.

Now, when you refresh the page, you should see a button that toggles between using text and emojis to say hello. It -should look something like this:

LiveViewJS Hello World Recording

Great start!

You've just created your first LiveView and added it to your webserver! There is a lot more to learn about -LiveViewJS, but you are well on your way. We recommend you continue to the -Anatomy of a LiveView section to start to learn more about how LiveViews work.

- - - - \ No newline at end of file diff --git a/docs/docs/quick-starts/deno-run-examples.html b/docs/docs/quick-starts/deno-run-examples.html deleted file mode 100644 index 4c0258f2..00000000 --- a/docs/docs/quick-starts/deno-run-examples.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -Deno - Run the Examples | LiveViewJS - - - - -
-

Deno - Run the Examples

LiveViewJS ships with over a dozen example LiveViews that show everything from simple button-based events to -real-time, multi-player views. It takes approximately 1 minute to get these examples up and running and is a good way to -get a feel for the user experience of a LiveView. Let's get started!

Prerequisite

Deno version 1.24.x or above. (Older versions may work but haven't been tested.)

If you haven't already, download the LiveViewJS repo.

Run the Examples

Navigate to the packages/deno directory:

# cd to the deno directory
cd packages/deno

Then, start the Deno server with the examples:

deno run --allow-run --allow-read --allow-env  src/example/autorun.ts

Point your browser to http://localhost:9001

Explore the Examples

You should see something like the screenshot below including a list of examples with a brief description, a link to the -running LiveView, and a link to the source code for each example.

LiveViewJS Examples Screenshot

- - - - \ No newline at end of file diff --git a/docs/docs/quick-starts/get-liveviewjs-repo.html b/docs/docs/quick-starts/get-liveviewjs-repo.html deleted file mode 100644 index 9f0e9fd0..00000000 --- a/docs/docs/quick-starts/get-liveviewjs-repo.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -Download the Repo | LiveViewJS - - - - -
-

Download the Repo

The fastest way to run the example or build your own LiveView is by downloading the LiveViewJS repo. This repo -contains all the examples and configured webserver code for Express (NodeJS) and Oak (Deno).

Get the Code

Either use git clone or degit to get the LiveViewJS GitHub repository.

info

degit is a lightweight way to clone a repo without the .git parts. -More info.

Clone the LiveViewJS GitHub repository:

# clone the LiveViewJS repo
git clone https://github.com/floodfx/liveviewjs.git

OR fetch with degit:

# copy the LiveViewJS repo
npx degit floodfx/liveviewjs liveviewjs

Change to the LiveViewJS Directory

# cd to the LiveViewJS directory
cd liveviewjs

Node or Deno?

LiveViewJS runs on both Node and Deno, but you'll probably want to start down one path or the other depending on what -runtime you are more familiar with or are already using.

note

The LiveViewJS library APIs are the same so you can build your LiveViews on one platform and run them on the -other unless you are using Deno or Node-specific APIs in your LiveView implementation.

Node

Deno

- - - - \ No newline at end of file diff --git a/docs/docs/quick-starts/nodejs-build-first-liveview.html b/docs/docs/quick-starts/nodejs-build-first-liveview.html deleted file mode 100644 index 1e8b10cc..00000000 --- a/docs/docs/quick-starts/nodejs-build-first-liveview.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - -NodeJS - Build a LiveView | LiveViewJS - - - - -
-

NodeJS - Build a LiveView

Since you've already downloaded the LiveViewJS repo, it should be easy to create a new -LiveView and add it to your webserver. Let's get started!

Create a new LiveView in Express

Since we are using Express to serve our LiveViews, we'll create a new LiveView in the packages/express directory.

Use your favorite editor to create a new file packages/express/src/example/liveview/hello.ts and add the following -code and hit save:

import { createLiveView, html } from "liveviewjs";

export const helloLiveView = createLiveView({
render: () => html`Hello World!`,
});

Congratulations! You've just created your first LiveView! It doesn't do much yet but let's get it running in the -browser.

Setup a new Route

Let's add a route to this LiveView to see it in our browser. Edit packages/express/src/example/index.ts and make the -following highlighted changes:

packages/express/src/example/index.ts
...
import { htmlPageTemplate, wrapperTemplate } from "./liveViewRenderers";
import { helloLiveView } from "./liveview/hello";

// map request paths to LiveViews
const router: LiveViewRouter = {
"/hello": helloLiveView,
"/autocomplete": autocompleteLiveView,
...

Great! We've now setup our new LiveView to be served at the /hello path. Let's start the server and see it in action.

Start the Express Server

First, load the NPM dependencies:

# install the NPM dependencies
npm install

Then, start the express server:

# start express server
npm run start -w packages/express
info

You will probably see a warning from NodeJS about using an experimental feature:

ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)

The feature we are using is the built-in fetch method. Feel free to ignore this warning.

See the LiveView in Action

Point your browser to http://localhost:4001/hello, and you should see something like the -following: LiveViewJS Hello World Screenshot

Next Steps

Ok, we got our first LiveView running but it isn't very interactive. Let's make it more interesting by adding a button -that toggles between using text and emojis to say hello. Update the hello.ts file to the following:

packages/express/src/example/liveview/hello.ts
import { createLiveView, html } from "liveviewjs";

export const helloLiveView = createLiveView({
mount: (socket) => {
socket.assign({ useEmoji: false });
},
handleEvent(event, socket) {
socket.assign({ useEmoji: !socket.context.useEmoji });
},
render: (context) => {
const msg = context.useEmoji ? "👋 🌎" : "Hello World";
return html`
${msg}
<br />
<button phx-click="toggle">Toggle Message</button>
`;
},
});

Now, when you refresh the page, you should see a button that toggles between using text and emojis to say hello. It -should look something like this:

LiveViewJS Hello World Recording

info

You'll notice that LiveViewJS automatically rebuilds and reloads the server when you make changes to your -LiveView code. This is a great way to iterate quickly on your LiveView.

Great start!

You've just created your first LiveView and added it to your webserver! There is a lot more to learn about -LiveViewJS but you are well on your way. We recommend you continue to the -Anatomy of a LiveView section to start to learn more about how LiveViews work.

- - - - \ No newline at end of file diff --git a/docs/docs/quick-starts/nodejs-run-examples.html b/docs/docs/quick-starts/nodejs-run-examples.html deleted file mode 100644 index c67b51be..00000000 --- a/docs/docs/quick-starts/nodejs-run-examples.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - -NodeJS - Run the Examples | LiveViewJS - - - - -
-

NodeJS - Run the Examples

LiveViewJS ships with over a dozen example LiveViews that show everything from simple button-based events to -real-time, multi-player views. It takes approximately ⏱ 1 minute to get these examples up and running and is a good way -to get a feel for the user experience of a LiveView. Let's get started!

Prerequisite

Node.js version 18.x or above.

note

We rely on the NodeJS Fetch API, which is only available in 18+.

If you haven't already, download the LiveViewJS repo.

Run the Examples

First, load the NPM dependencies:

# install the NPM dependencies
npm install

Then, start the express server with the examples:

# run the examples
npm run start -w packages/express

Point your browser to http://localhost:4001

Explore the Examples

You should see something like the screenshot below including a list of examples with a brief description, a link to the -running LiveView, and a link to the source code for each example.

LiveViewJS Examples Screenshot

- - - - \ No newline at end of file diff --git a/docs/docs/real-time-multi-player-pub-sub/example-pub-sub.html b/docs/docs/real-time-multi-player-pub-sub/example-pub-sub.html deleted file mode 100644 index fef52060..00000000 --- a/docs/docs/real-time-multi-player-pub-sub/example-pub-sub.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - -Example Pub/Sub LiveView | LiveViewJS - - - - -
-

Example Pub/Sub LiveView

We're going to extend our counter example from learning the LiveView API to -use Pub/Sub which will make it a real-time, multi-player counter. Here is the code with the Pub/Sub changes highlighted:

realtimeCounterLiveView.ts
import { createLiveView, html, SingleProcessPubSub } from "liveviewjs";

// An in-memory count simulating state outside of the LiveView
let count = 0;
// Use a single process pub/sub implementation (for simplicity)
const pubSub = new SingleProcessPubSub();

/**
* A basic counter that increments and decrements a number.
*/
export const rtCounterLiveView = createLiveView<
{ count: number }, // Define LiveView Context / State
{ type: "increment" } | { type: "decrement" }, // Define LiveView Events
{ type: "counter"; count: number } // Define LiveView Info messages
>({
mount: (socket) => {
// init state, set count to current count
socket.assign({ count });
// subscribe to counter events
socket.subscribe("counter");
},
handleEvent: (event, socket) => {
// handle increment and decrement events
const { count } = socket.context;
switch (event.type) {
case "increment":
// broadcast the new count
pubSub.broadcast("counter", { count: count + 1 });
break;
case "decrement":
// broadcast the new count
pubSub.broadcast("counter", { count: count - 1 });
break;
}
},
handleInfo: (info, socket) => {
// receive updates from pubsub and update the context
count = info.count;
socket.assign({ count });
},
render: async (context) => {
// render the view based on the state
const { count } = context;
return html`
<div>
<h1>Count is: ${count}</h1>
<button phx-click="decrement">-</button>
<button phx-click="increment">+</button>
</div>
`;
},
});

How it works

  • This works just like the counter.ts example except we're using Pub/Sub to broadcast the new count to all connected -clients and subscribe to updates from other clients.
  • When a client clicks the increment or decrement button, we broadcast the new count to all connected clients using -pubSub.broadcast.
  • The LiveViewJS framework automatically routes messages from pubSub.broadcast to the handleInfo function for -any LiveView subscribed to the topic.
  • In this case, handleInfo receives the new count and updates the LiveView context which re-renders the view.

It's that easy!

In ~10 lines of code we've built a real-time, multi-player counter! Sure a real-time counter isn't particularly useful -but shows you how easy it is to create real-time, multi-player applications with very little code and very little -effort.

- - - - \ No newline at end of file diff --git a/docs/docs/real-time-multi-player-pub-sub/overview.html b/docs/docs/real-time-multi-player-pub-sub/overview.html deleted file mode 100644 index 2d0f0b50..00000000 --- a/docs/docs/real-time-multi-player-pub-sub/overview.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - -Overview | LiveViewJS - - - - -
-

Overview

LiveViewJS natively supports real-time, multi-player user experiences. This is because LiveViewJS (and Phoenix -LiveView for that matter) are built on top of Pub/Sub primatives.

Pub/Sub is a common pattern for decoupling processes by allowing messages to be sent to a topic by one process and -received asynchronously by another. In LiveViewJS, pub/sub is used to enable real-time, multi-player web application.

Pub/Sub in LiveViewJS

There are two main ways to enable real-time, multi-player experiences in LiveViewJS:

  • subscribe to topics - If you have a LiveView that needs to receive messages from a topic, you can subscribe to the -topic in the mount method and receive "Info" messages in the handleInfo method.
  • broadcast to topics - If you have a LiveView (or data model) that needs to send messages to a topic, you can -broadcast to the topic using an implementation of the PubSub interface.
info

LiveViewJS ships with three implementations of the PubSub interface:

  • SingleProcessPubSub - A pub/sub implementation (backed by EventEmitter that is useful for testing and development -(as it only works in a single process).
  • RedisPubSub - A pub/sub implementation that uses Redis as the pub/sub backend. This is useful for production -deployments that are running on NodeJS
  • BroadcastChannelPubSub - A pub/sub implementation that uses the BroadcastChannel API which is a server enabled -pub/sub implementation that is useful for production deployments on Deno Deploy.

Subscribe to Topics

If you have a LiveView that needs to receive messages from a topic, you can subscribe to the topic in the mount method -and receive "Info" messages in the handleInfo method.

For example:

...
mount: (socket) => {
// subscribe to Info events on the "my_topic" topic
socket.subscribe("my_topic");
},
...

This will cause the handleInfo method to be called with any messages that are broadcast to the "my_topic" topic.

...
handleInfo: (info, socket) => {
// handle info messages
switch (info.type) {
...
case "my_topic":
// handle my_topic messages
break;
...
}
},
...

Broadcast to Topics

If you have a LiveView (or data model) that needs to send messages to a topic, you can broadcast to the topic using an -implementation of the PubSub interface.

For example:

...
// create a pubsub instance
const pubSub = new SingleProcessPubSub();
...
// broadcast a message to the "my_topic" topic
pubSub.publish("my_topic", { message: "Hello World" });
...

This will cause the handleInfo method to be called with any messages that are broadcast to the "my_topic" topic.

Connecting with other LiveViews

It should be clear at this point that if you want to connect LiveView with a different LiveView (either the same type or -a different type) all you need to do is broadcast a message to a topic that the other LiveView is subscribed to.

Connecting with External Events

Let's say you have an event that is happening outside of the LiveViewJS. All you have to do is connect that event with -the LiveViewJS by broadcasting a message to a topic that the LiveViewJS is subscribed to. This means you either -need to use the PubSub implementation (with the same configuration) in that different code or you need to use the same -pub/sub backend (e.g., Redis or BroadcastChannel).

- - - - \ No newline at end of file diff --git a/docs/docs/user-events-slash-bindings/additional-bindings.html b/docs/docs/user-events-slash-bindings/additional-bindings.html deleted file mode 100644 index 9e12846f..00000000 --- a/docs/docs/user-events-slash-bindings/additional-bindings.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - -Additional Bindings | LiveViewJS - - - - -
-

Additional Bindings

There are additional bindings outside of the four main bindings for User Events that you will find extremely useful and -that you will use often.

Value Bindings

When you need to send along additional data with an event binding you can use a "value binding" which looks something -like phx-value-[NAME] where [NAME] is replaced by the key of the value you want to pass. This binding can be used in -conjunction with other click, key, and focus bindings.

Value Binding Example

For example let's say you want to send the mark_complete event to the server along with and id value -(e.g., {id: "myId"}) when the user clicks on the "Complete" button. To do this you do the following:

<button phx-click="mark_complete" phx-value-id="myId">Complete</button>

Note the [NAME] part of phx-value-[NAME] is id used as the object key while the attribute value (i.e., "myId") is -used as the object value.

This example would send the following event to the server:

{
type: "mark_complete",
id: "myId"
}
- - - - \ No newline at end of file diff --git a/docs/docs/user-events-slash-bindings/bindings-table.html b/docs/docs/user-events-slash-bindings/bindings-table.html deleted file mode 100644 index 58b27cda..00000000 --- a/docs/docs/user-events-slash-bindings/bindings-table.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -Phoenix LiveView Bindings | LiveViewJS - - - - -
-

Phoenix LiveView Bindings

Here is a table of all the bindings available in Phoenix LiveView and whether they are available in LiveViewJS.

info

These bindings actually come from Phoenix LiveView since -we use the same client-side JavaScript library. The table below denotes which bindings are "Supported" in LiveViewJS -and which are not. Bindings below marked with ✅ are working and tested and most of them have example usage in the -examples codebase. Those with ?, we have not gotten around to testing so not sure if they work. Those marked with ❌ -are not yet implemented and known not to work.

BindingAttributeSupported
Paramsphx-value-*
Click Eventsphx-click
Click Eventsphx-click-away
Form Eventsphx-change
Form Eventsphx-submit
Form Eventsphx-feedback-for
Form Eventsphx-disable-with
Form Eventsphx-trigger-action
Form Eventsphx-auto-recover
Focus Eventsphx-blur
Focus Eventsphx-focus
Focus Eventsphx-window-blur
Focus Eventsphx-window-focus
Key Eventsphx-keydown
Key Eventsphx-keyup
Key Eventsphx-window-keydown
Key Eventsphx-window-keyup
Key Eventsphx-key
DOM Patchingphx-update
DOM Patchingphx-remove
JS Interopphx-hook
Rate Limitingphx-debounce
Rate Limitingphx-throttle
Static Trackingphx-track-static
- - - - \ No newline at end of file diff --git a/docs/docs/user-events-slash-bindings/overview.html b/docs/docs/user-events-slash-bindings/overview.html deleted file mode 100644 index 5692f181..00000000 --- a/docs/docs/user-events-slash-bindings/overview.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - -User Events / Bindings | LiveViewJS - - - - -
-

User Events / Bindings

Four main User Events

There are four main types of user events that a LiveView can listen to and respond to:

  • Click events
  • Form events
  • Key events
  • Focus events

To listen for user events there are a set of "bindings" (a.k.a. attributes) that you add to the HTML elements in your -LiveView render method.

Click Events

User clicks are the most common type of user event and there are two types of click bindings:

  • phx-click - Add this binding to an HTML element (e.g., <... phx-click="myEvent" ...>) and when a user clicks on the -element the event (i.e., value of the attribute) will be sent to the server.
  • phx-click-away - This binding is similar to phx-click except that an event will occur when the user clicks outside -of the element.

Click Event Example

Let's say we want to send the increment event to the server when the user clicks on the "+" button:

<button phx-click="increment">+</button>

The handleEvent method would receive an event that looks like this:

{
type: "increment";
}

Form Events

Form events are triggered by the user interacting with form inputs. There are two types of form bindings:

  • phx-change - When a user changes the value of a form element, the event named by the phx-change attribute will be -sent to the server along with all the form values. This is typically used for form validation purposes prior to form -submission.
  • phx-submit - This binding is initiated when a user submits a form and the event named by the phx-submit attribute -will be sent to the server along with all the form values.
<form action="#" phx-change="validate" phx-submit="save">...</form>

Adding phx-change to a form means that any time a user changes the value of a form element, the validate event will -be sent to the server along with the form values. Typically, phx-change handles form validation prior to form -submission.

Adding phx-submit to a form means that when the user submits the form, the save event will be sent to the server -along with all the form values.

info

In LiveViewJS, Forms are typically used in conjunction with -LiveViewChangesets. LiveViewChangesets are designed to work together with -form events to make form validation and submission easy and powerful. We'll dive into more details later on in the next -section.

Key Events

Key events are triggered by the user pressing a key on the keyboard. There are key bindings for both the element-level -and the window-level:

  • phx-keydown, phx-window-keydown - When a user presses a key down on the keyboard, the event named by the attribute -will be sent to the server along with the key that was pressed.
  • phx-keyup, phx-window-keyup - When a user releases a key on the keyboard, the event named by the attribute will be -sent to the server along with the key that was released.

phx-key is an optional attribute which limits triggering of the key events to the key provided in the attribute (e.g. -phx-key="ArrowUp"). You can find a list of the keys on -MDN Keyboard Values.

Key Event Example

Let's say we want to send the key_event event to the server along with the {key: "ArrowUp"} payload when the user -presses the "ArrowUp" key:

<div phx-window-keydown="key_event" phx-key="ArrowUp" />

The handleEvent method would receive an event that looks like this:

{
type: "key_event",
key: "ArrowUp"
}

Focus / Blur Events

If a HTML element emits focus and blur events, you can use the following bindings to send events to the server upon -focus and/or blur:

  • phx-focus - When a user focuses on an element, the event named by the phx-focus attribute will be sent to the -server.
  • phx-blur - When a user blurs from an element, the event named by the phx-blur attribute will be sent to the -server.

Similar to key events, there are window-level and element-level bindings:

  • phx-window-focus - When a user focuses on the window, the event named by the phx-window-focus attribute will be -sent to the server.
  • phx-window-blur - When a user blurs from the window, the event named by the phx-window-blur attribute will be sent -to the server.

Focus / Blur Examples

Let's say we want to send the focus_event event to the server when the user focuses on the input and the blur_event -event when the user blurs from the input

<input name="text" phx-focus="focus_event" phx-blur="blur_event" />

The handleEvent method would receive an event that looks like this on focus:

{
type: "focus_event";
}

The handleEvent method would receive an event that looks like this on blur:

{
type: "blur_event";
}

Additional Bindings

There are other bindings that provide additional functionality for your LiveView and work in conjunction with the event -bindings we reviewed above.

- - - - \ No newline at end of file diff --git a/docs/docs/user-events-slash-bindings/rate-limiting-bindings.html b/docs/docs/user-events-slash-bindings/rate-limiting-bindings.html deleted file mode 100644 index 1be10b28..00000000 --- a/docs/docs/user-events-slash-bindings/rate-limiting-bindings.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - -Rate Limiting Bindings | LiveViewJS - - - - -
-

Rate Limiting Bindings

Deboucing and throttling user events is a very common need and to support these use-cases there are the following -bindings:

  • phx-debounce - Debounce an event by the number of milliseconds specified in the attribute value or by setting -the value to blur. This is useful for preventing multiple events from being sent to the server in rapid succession. -When blur is the value, the event will be debounced until the element is blurred by the user. Typically used for -input elements.
  • phx-throttle - Throttle an event by the number of milliseconds specified in the attribute value. In contrast to -debouncing, throttling emits an event immediately and then only once every specified number of milliseconds. Typically -used to rate limit click events, key events, and mouse actions.

Debounce Examples

Here are some examples of the debounce binding in action.

Debounce input blur

Let's say we want to send the validate event to the server when a user blurs away from the address input in a form:

<form action="#" phx-change="validate" phx-submit="save">
<!--// only send "validate" event when address input is blurred -->
<input name="address" phx-debounce="blur" />
</form>

Debouce input change

Let's say we only want to send the search event to the server 1 second after a user stops typing into the search -input:

<form action="#" phx-change="search">
<!--// send "search" event after 1 second of debouncing -->
<input name="query" phx-debounce="1000" />
</form>

Throttle Example

Let's say we only want to send one volume_up event every 500ms. The user can click the button as many times as they -want, but the event will only be sent to the server every 500ms:

<!--// rate limit clicks on a volume up event -->
<button phx-click="volume_up" phx-throttle="500" />
- - - - \ No newline at end of file diff --git a/docs/docs/webserver-integration/liveview-server-adaptor.html b/docs/docs/webserver-integration/liveview-server-adaptor.html deleted file mode 100644 index 37df99dd..00000000 --- a/docs/docs/webserver-integration/liveview-server-adaptor.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - -LiveViewServerAdaptor | LiveViewJS - - - - -
-

LiveViewServerAdaptor

LiveViewJS provides an interface that you can implement to integrate with your webserver of choice. This interface is -called LiveViewServerAdaptor. This is the interface that is implemented by the NodeExpressLiveViewServer (and -DenoOakLiveViewServer).

Required Methods

LiveViewServerAdaptor requires that you implement the following methods:

  • httpMiddleware()
  • wsMiddleware()

Digging into NodeExpressLiveViewServer

The implementation behind the NodeExpressLiveViewServer is where the magic of mapping HTTP and websocket requests to -LiveViewJS routes happens.

HTTP Middleware

Let's look at the ExpressJS implementation of the httpMiddleware method:

httpMiddleware(): RequestHandler {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const adaptor = new ExpressRequestAdaptor(req, res, this.serDe);
const { getRequestPath } = adaptor;

// look up LiveView for route
const liveview = this.router[getRequestPath()];
if (!liveview) {
// no LiveView found for route so call next() to
// let a possible downstream route handle the request
next();
return;
}

// defer to liveviewjs to handle the request
const rootViewHtml = await handleHttpLiveView(
nanoid,
nanoid,
liveview,
adaptor,
this.htmlPageTemplate,
this.liveTitleOptions,
this.wrapperTemplate
);

// check if LiveView calls for a redirect and if so, do it
if (adaptor.redirect) {
res.redirect(adaptor.redirect);
return;
}

// otherwise render the LiveView HTML
res.format({
html: () => {
res.send(rootViewHtml);
},
});
} catch (error) {
next(error);
}
};
}

In summary, the httpMiddleware method does the following:

  1. It creates an ExpressRequestAdaptor instance that wraps the ExpressJS Request and Response objects so they can -be used by LiveViewJS.
  2. It uses the LiveViewRouter to see if there is a LiveView registered for the request path.
  3. If there is a LiveView registered for the request path, it calls handleHttpLiveView to handle the request. -handleHttpLiveView is provided by LiveViewJS that connect the request to the LiveView.
  4. If there is no LiveView registered for the request path, it calls next() to let the next middleware in the chain -handle the request.
  5. We check for redirects and if there is one, we do it.
  6. Otherwise, we render the LiveView HTML.

Websocket Middleware

Let's look at the ExpressJS implementation of the wsMiddleware method:

wsMiddleware(): (wsServer: WebSocketServer) => Promise<void> {
return async (wsServer: WebSocketServer) => {
// send websocket requests to the LiveViewJS message router
wsServer.on("connection", (ws) => {
const connectionId = nanoid();
ws.on("message", async (message, isBinary) => {
// pass websocket messages to LiveViewJS
await this._wsRouter.onMessage(connectionId, message, new NodeWsAdaptor(ws), isBinary);
});
ws.on("close", async () => {
// pass websocket close events to LiveViewJS
await this._wsRouter.onClose(connectionId);
});
});
};
}

In summary, the wsMiddleware method listens for websocket connections, messages, and close events and passes them to -the LiveViewJS message router. The wsRouter knows how to route websocket messages to the correct LiveView and handle -the websocket lifecycle. Not much to see here since it's all handled by LiveViewJS.

That's more or less it modulo the ExpressRequestAdaptor. The ExpressRequestAdaptor is a wrapper around the ExpressJS -Request and Response objects that provides a common interface for LiveViewJS to use. This is another necessary step -to normalize any differences between webserver implementations.

- - - - \ No newline at end of file diff --git a/docs/docs/webserver-integration/overview.html b/docs/docs/webserver-integration/overview.html deleted file mode 100644 index 481bafeb..00000000 --- a/docs/docs/webserver-integration/overview.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - -Webserver Integration | LiveViewJS - - - - -
-

Webserver Integration

Out of the box, LiveViewJS supports two webserver integrations:

  • ExpressJS (NodeJS)
  • Oak (Deno)

Both integrations are very similar and are based on the same core API and intgration points between the webserver and -the LiveViewJS library.

How the Integration Works

As we've covered elsewhere, LiveViewJS handles both HTTP and Websocket connections for routes that are registered with -it. It does this by providing "middleware" (for HTTP and websocket traffic) that is plugged into the webserver.

This middleware knows how to handle the HTTP and websocket traffic for the routes that are registered with LiveViewJS.

Let's look at an example of how this works in ExpressJS.

// create our express app
const app = express();
...
// whatever other express setup you need to do
...
// setup our LiveViewJS routes
const router: LiveViewRouter = {
...
"/hello": helloLiveView,
...
};
...
// initialize the NodeJS LiveViewServer
const liveView = new NodeExpressLiveViewServer(
router,
htmlPageTemplate, // HTML template for all liveviews
{ title: "Express Demo", suffix: " · LiveViewJS" }, // live tag options
);

// setup the LiveViewJS HTTP middleware
app.use(liveView.httpMiddleware());


// configure express to handle both http and websocket requests
const httpServer = new Server();
const wsServer = new WebSocketServer({
server: httpServer,
});

// send http requests to the express app
httpServer.on("request", app);

// setup the LiveViewJS websocket middleware
const liveViewWsMiddleware = liveView.wsMiddleware();
liveViewWsMiddleware(wsServer);
...

Recap of the Integration Points

Essentially, we do some LiveViewJS configuration, then we plug the LiveViewJS middleware into the webserver and -websocket server.

When traffic comes in, the webserver will pass the request to the LiveViewJS middleware. The middleware checks if the -request is for a LiveViewJS route. If it is, it will handle the request. If it isn't, it will pass the request to the -next middleware in the chain.

Let's look at the integration points in more detail in the next sections.

- - - - \ No newline at end of file diff --git a/docs/docs/webserver-integration/support-webserver-x.html b/docs/docs/webserver-integration/support-webserver-x.html deleted file mode 100644 index cccb8ece..00000000 --- a/docs/docs/webserver-integration/support-webserver-x.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - -Support Webserver "X" | LiveViewJS - - - - -
-

Support Webserver "X"

If you want to use LiveViewJS with a webserver that is not supported out of the box, you can implement the -LiveViewServerAdaptor interface and plug it into your webserver.

Essentially, you'll need to be able to intercept HTTP and websocket requests and pass them to the LiveViewJS library. -The LiveViewJS library will then handle the requests and return the appropriate responses.

Look at the existing integrations

Checkout the LiveViewJS source code and look at the -NodeExpressLiveViewServer and -DenoOakLiveViewServer classes. -These are the two webserver integrations that are supported out of the box.

Open an issue

We are happy to help you get LiveViewJS working with your webserver. If you -open an issue on the LiveViewJS GitHub repo, we'll be happy to support -you.

- - - - \ No newline at end of file diff --git a/docs/fonts/LibreFranklin-Italic-VariableFont_wght.ttf b/docs/fonts/LibreFranklin-Italic-VariableFont_wght.ttf deleted file mode 100644 index ccb93cc9..00000000 Binary files a/docs/fonts/LibreFranklin-Italic-VariableFont_wght.ttf and /dev/null differ diff --git a/docs/fonts/LibreFranklin-VariableFont_wght.ttf b/docs/fonts/LibreFranklin-VariableFont_wght.ttf deleted file mode 100644 index a0b76eb2..00000000 Binary files a/docs/fonts/LibreFranklin-VariableFont_wght.ttf and /dev/null differ diff --git a/docs/img/diagrams/liveview-lifecycle-heartbeat.svg b/docs/img/diagrams/liveview-lifecycle-heartbeat.svg deleted file mode 100644 index d8a06b87..00000000 --- a/docs/img/diagrams/liveview-lifecycle-heartbeat.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewAutomatic Health ChecksLiveView automatically sends heartbeat messages to ensure client and server are connectedalt[1. Connection Healthy][2. Connection Lost]loop[Heartbeat]Heartbeat Ping (30 sec)1ACK2No response3Automatic Retry4Automatic Recover5ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/img/diagrams/liveview-lifecycle-http-phase.svg b/docs/img/diagrams/liveview-lifecycle-http-phase.svg deleted file mode 100644 index 68e9ba7a..00000000 --- a/docs/img/diagrams/liveview-lifecycle-http-phase.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewHTTP Request PhaseThe initial phase is rendering the LiveView as HTMLAdvantages:- "First paint" is extremely fast (it's just HTML)- No waiting for MBs of JS to download- Search engine friendly (again it is only HTML)- Renders if JS is disabledInitialize and Render:call `mount`, `handleParams`, and `render`Wrap specific LiveView HTML with common page HTMLClient receives fully rendered LiveView HTMLHTTP RequestGET '/my-liveview'1Route to LiveView'/my-liveview => MyLiveView'2`mount`3`handleParams`4`render`5LiveView HTML<div>...</div>6Page HTML7HTTP Response<html>...</html>8ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/img/diagrams/liveview-lifecycle-shutdown.svg b/docs/img/diagrams/liveview-lifecycle-shutdown.svg deleted file mode 100644 index 1ab40852..00000000 --- a/docs/img/diagrams/liveview-lifecycle-shutdown.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewShutdownWhen user leaves the page or connection is lost and irrecoverable, LiveViewJS "cleans up" any resources for that sessionRelease temporary (in memory) datashutdown any intervals, etcRelease temporary (in memory) datashutdown any intervals, etcalt[User Leaves Page][Connection Irrecoverable]opt[Shutdown]User leaves page1Clean Up LiveView2LiveView Resources Released3No Heartbeat Response4Clean Up LiveView5LiveView Resources Released6ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/img/diagrams/liveview-lifecycle-user-and-server-events.svg b/docs/img/diagrams/liveview-lifecycle-user-and-server-events.svg deleted file mode 100644 index a76a0a42..00000000 --- a/docs/img/diagrams/liveview-lifecycle-user-and-server-events.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewInteractiveThe LiveView is interactive and can respond to user events or update based on server eventsAll user events follow the same lifecycle process. Route event to `handleEvent` and then `render` and calculate the Dynamics Tree DiffHandle event:call `handleEvent` then `render``handleEvent` may kick off additionalserver events (i.e. `sendInfo`) but thoseevents are asynchronous to this event loopLiveViewJS calculates differences betweenprevious Dynamics and current Dynamicsand ships back only changesSince the Static part of the HTML never changes, LiveView only sends back any "dynamic" that may have changed based on the eventopt[User Events]Server Events:LiveViewJS automatically routes server eventsfrom `socket.sendInfo` or Pub/Subsubscriptions to `handleInfo` to the correctLiveView instanceLiveViewJS calculates differences betweenprevious Dynamics and current Dynamicsand ships back only changesSince the Static part of the HTML never changes, LiveView only sends back any "dynamic" that may have changed based on the eventopt[Server Events]phx-click, phx-submit,phx-blur, phx-keydown, etc1Route to LiveView2`handleEvent`3`render`4LiveView HTML<div>...</div>5Calculate Dynamics Tree Diff6Dynamics Tree Patch7Route Server Events8`handleInfo`9`render`10LiveView HTML<div>...</div>11Calculate Dynamics Tree Diff12Dynamics Tree Patch13ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/img/diagrams/liveview-lifecycle-websocket-join.svg b/docs/img/diagrams/liveview-lifecycle-websocket-join.svg deleted file mode 100644 index bc94732d..00000000 --- a/docs/img/diagrams/liveview-lifecycle-websocket-join.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerLiveViewWebsocket Join:Establish the websocket connection and run the initial LiveView lifecycle methodsInitialize and Render:call `mount`, `handleParams`, and `render`Statics/Dynamics Tree intelligentlytracks parts of HTML that won't change (statics)and those that can (dynamics).This data structure enables optimized diffsto be shipped back to the client.Websocket is established, Statics/Dynamics Treesetup and LiveView is ready for interactive phase.Websocket Connect1Route to LiveView2`mount`3`handleParams`4`render`5LiveView HTML<div>...</div>6Build Statics/Dynamics Tree7Full Statics/Dynamics Tree8ClientLiveViewServerLiveView \ No newline at end of file diff --git a/docs/img/diagrams/liveviewjs-lifecycle.svg b/docs/img/diagrams/liveviewjs-lifecycle.svg deleted file mode 100644 index fa5eda5a..00000000 --- a/docs/img/diagrams/liveviewjs-lifecycle.svg +++ /dev/null @@ -1 +0,0 @@ -ClientLiveViewServerInitial Request"First paint" is extremely fast (Only sending HTML not MBs of Javascript)Websocket SessionDOM Patch is a diff between current HTML and updated HTML(These diffs are extremely small and efficient)Subsequent User Events are sent over Websocketopt[Click Events]opt[Form Change Events]opt[Blur/Focus Events]opt[Key Events]Event's are sent over Websocket, the server runs the business logic, calculates the diff then sends them back to the clientServer Event spawns updateopt[PushPatch]Server can initiate updates for server events, Pub/Sub, and/or updates from other clientsAutomatic Health Checksloop[Heartbeat]Health checks are automatic with auto-rejoin attempted if connection lostHTTP RequestHTTP ResponseWebsocket ConnectDOM Patchphx-click, phx-click-away, etcDOM Patchphx-change, phx-submitDOM Patchphx-blur, phx-focus, etcDOM Patchphx-keydown, phx-keyup, etcDOM PatchServer-side EventDOM PatchHeartbeat Ping (30 sec)Heartbeat ACKClientLiveViewServer diff --git a/docs/img/favicon.ico b/docs/img/favicon.ico deleted file mode 100644 index b6c56d25..00000000 Binary files a/docs/img/favicon.ico and /dev/null differ diff --git a/docs/img/features/multiplayer.svg b/docs/img/features/multiplayer.svg deleted file mode 100644 index e9ff766c..00000000 --- a/docs/img/features/multiplayer.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/docs/img/features/nirvana.svg b/docs/img/features/nirvana.svg deleted file mode 100644 index aa88959c..00000000 --- a/docs/img/features/nirvana.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/img/features/simple.svg b/docs/img/features/simple.svg deleted file mode 100644 index 35019210..00000000 --- a/docs/img/features/simple.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/img/logo.svg b/docs/img/logo.svg deleted file mode 100644 index f086b045..00000000 --- a/docs/img/logo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/img/screenshots/liveviewjs_counter_liveview_rec.gif b/docs/img/screenshots/liveviewjs_counter_liveview_rec.gif deleted file mode 100644 index f7e99a2d..00000000 Binary files a/docs/img/screenshots/liveviewjs_counter_liveview_rec.gif and /dev/null differ diff --git a/docs/img/screenshots/liveviewjs_examples_rec.gif b/docs/img/screenshots/liveviewjs_examples_rec.gif deleted file mode 100644 index 41d53712..00000000 Binary files a/docs/img/screenshots/liveviewjs_examples_rec.gif and /dev/null differ diff --git a/docs/img/screenshots/liveviewjs_hello_liveview.png b/docs/img/screenshots/liveviewjs_hello_liveview.png deleted file mode 100644 index c99a9ad4..00000000 Binary files a/docs/img/screenshots/liveviewjs_hello_liveview.png and /dev/null differ diff --git a/docs/img/screenshots/liveviewjs_hello_liveview_deno.png b/docs/img/screenshots/liveviewjs_hello_liveview_deno.png deleted file mode 100644 index 53d5bb1f..00000000 Binary files a/docs/img/screenshots/liveviewjs_hello_liveview_deno.png and /dev/null differ diff --git a/docs/img/screenshots/liveviewjs_hello_toggle_liveview_deno_rec.gif b/docs/img/screenshots/liveviewjs_hello_toggle_liveview_deno_rec.gif deleted file mode 100644 index a5d9ebbb..00000000 Binary files a/docs/img/screenshots/liveviewjs_hello_toggle_liveview_deno_rec.gif and /dev/null differ diff --git a/docs/img/screenshots/liveviewjs_hello_toggle_liveview_rec.gif b/docs/img/screenshots/liveviewjs_hello_toggle_liveview_rec.gif deleted file mode 100644 index bd1ab383..00000000 Binary files a/docs/img/screenshots/liveviewjs_hello_toggle_liveview_rec.gif and /dev/null differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 131707a0..00000000 --- a/docs/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - -LiveViewJS | LiveViewJS - - - - -
-

LiveViewJS

Simple yet powerful framework for LiveViews in NodeJS and Deno

Simple, powerful, scalable paradigm

Create "single page app" user experiences with the ease of server-rendered HTML.

Native Real-time & multi-player support

Easily update the UI of any or all connected users with built-in support for Pub/Sub.

No boilerplate & no reinventing the wheel

No "client-side routing" or "state management"; no REST or GraphQL APIs; No BS, just GSD-nirvana.

- - - - \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml deleted file mode 100644 index 7bf5f8cd..00000000 --- a/docs/sitemap.xml +++ /dev/null @@ -1 +0,0 @@ -https://www.liveviewjs.com/blogweekly0.5https://www.liveviewjs.com/blog/archiveweekly0.5https://www.liveviewjs.com/blog/first-blog-postweekly0.5https://www.liveviewjs.com/blog/long-blog-postweekly0.5https://www.liveviewjs.com/blog/mdx-blog-postweekly0.5https://www.liveviewjs.com/blog/tagsweekly0.5https://www.liveviewjs.com/blog/tags/docusaurusweekly0.5https://www.liveviewjs.com/blog/tags/facebookweekly0.5https://www.liveviewjs.com/blog/tags/helloweekly0.5https://www.liveviewjs.com/blog/tags/holaweekly0.5https://www.liveviewjs.com/blog/welcomeweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/handle-event-detailsweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/handle-infoweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/handle-info-background-taskweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/handle-info-pub-subweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/handle-info-user-initiatedweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/handle-paramsweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/liveview-apiweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/mount-detailsweekly0.5https://www.liveviewjs.com/docs/anatomy-of-a-liveview/render-detailsweekly0.5https://www.liveviewjs.com/docs/category/anatomy-of-a-liveviewweekly0.5https://www.liveviewjs.com/docs/category/client-side-javascriptweekly0.5https://www.liveviewjs.com/docs/category/forms--changesetsweekly0.5https://www.liveviewjs.com/docs/category/js-commandsweekly0.5https://www.liveviewjs.com/docs/category/lifecycle-of-a-liveviewweekly0.5https://www.liveviewjs.com/docs/category/liveviewsocketweekly0.5https://www.liveviewjs.com/docs/category/miscellaneousweekly0.5https://www.liveviewjs.com/docs/category/overviewweekly0.5https://www.liveviewjs.com/docs/category/quick-startsweekly0.5https://www.liveviewjs.com/docs/category/real-time--multi-playerweekly0.5https://www.liveviewjs.com/docs/category/uploading-filesweekly0.5https://www.liveviewjs.com/docs/category/user-eventsweekly0.5https://www.liveviewjs.com/docs/category/webserver-integrationsweekly0.5https://www.liveviewjs.com/docs/client-javascript/client-hooksweekly0.5https://www.liveviewjs.com/docs/client-javascript/example-hookweekly0.5https://www.liveviewjs.com/docs/client-javascript/overviewweekly0.5https://www.liveviewjs.com/docs/file-upload/drag-and-dropweekly0.5https://www.liveviewjs.com/docs/file-upload/image-previewweekly0.5https://www.liveviewjs.com/docs/file-upload/overviewweekly0.5https://www.liveviewjs.com/docs/file-upload/upload-config-optionsweekly0.5https://www.liveviewjs.com/docs/forms-and-changesets/changesetsweekly0.5https://www.liveviewjs.com/docs/forms-and-changesets/overviewweekly0.5https://www.liveviewjs.com/docs/forms-and-changesets/use-with-formsweekly0.5https://www.liveviewjs.com/docs/js-commands/add-remove-classweekly0.5https://www.liveviewjs.com/docs/js-commands/dispatch-cmdweekly0.5https://www.liveviewjs.com/docs/js-commands/overviewweekly0.5https://www.liveviewjs.com/docs/js-commands/push-cmdweekly0.5https://www.liveviewjs.com/docs/js-commands/set-remove-attrweekly0.5https://www.liveviewjs.com/docs/js-commands/show-hide-toggle-elweekly0.5https://www.liveviewjs.com/docs/js-commands/transition-cmdweekly0.5https://www.liveviewjs.com/docs/lifecycle-of-a-liveview/introweekly0.5https://www.liveviewjs.com/docs/liveview-socket/liveviewsocket-apiweekly0.5https://www.liveviewjs.com/docs/liveview-socket/liveviewsocket-api-contextweekly0.5https://www.liveviewjs.com/docs/liveview-socket/liveviewsocket-api-infosweekly0.5https://www.liveviewjs.com/docs/liveview-socket/liveviewsocket-api-miscweekly0.5https://www.liveviewjs.com/docs/liveview-socket/liveviewsocket-api-pushweekly0.5https://www.liveviewjs.com/docs/liveview-socket/liveviewsocket-api-uploadsweekly0.5https://www.liveviewjs.com/docs/liveview-socket/raw-liveviewsocket-apiweekly0.5https://www.liveviewjs.com/docs/misc/debugging-wireweekly0.5https://www.liveviewjs.com/docs/misc/flashweekly0.5https://www.liveviewjs.com/docs/misc/livecomponentsweekly0.5https://www.liveviewjs.com/docs/misc/root-and-page-renderersweekly0.5https://www.liveviewjs.com/docs/misc/security-topicsweekly0.5https://www.liveviewjs.com/docs/misc/statics-and-dynamicsweekly0.5https://www.liveviewjs.com/docs/overview/feedbackweekly0.5https://www.liveviewjs.com/docs/overview/gratitudeweekly0.5https://www.liveviewjs.com/docs/overview/introductionweekly0.5https://www.liveviewjs.com/docs/overview/paradigmweekly0.5https://www.liveviewjs.com/docs/overview/runtimesweekly0.5https://www.liveviewjs.com/docs/quick-starts/deno-build-first-liveviewweekly0.5https://www.liveviewjs.com/docs/quick-starts/deno-run-examplesweekly0.5https://www.liveviewjs.com/docs/quick-starts/get-liveviewjs-repoweekly0.5https://www.liveviewjs.com/docs/quick-starts/nodejs-build-first-liveviewweekly0.5https://www.liveviewjs.com/docs/quick-starts/nodejs-run-examplesweekly0.5https://www.liveviewjs.com/docs/real-time-multi-player-pub-sub/example-pub-subweekly0.5https://www.liveviewjs.com/docs/real-time-multi-player-pub-sub/overviewweekly0.5https://www.liveviewjs.com/docs/user-events-slash-bindings/additional-bindingsweekly0.5https://www.liveviewjs.com/docs/user-events-slash-bindings/bindings-tableweekly0.5https://www.liveviewjs.com/docs/user-events-slash-bindings/overviewweekly0.5https://www.liveviewjs.com/docs/user-events-slash-bindings/rate-limiting-bindingsweekly0.5https://www.liveviewjs.com/docs/webserver-integration/liveview-server-adaptorweekly0.5https://www.liveviewjs.com/docs/webserver-integration/overviewweekly0.5https://www.liveviewjs.com/docs/webserver-integration/support-webserver-xweekly0.5https://www.liveviewjs.com/weekly0.5 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b2103a90..44058406 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ }, "apps/lambda-examples": { "name": "@liveviewjs/lambda-examples", - "version": "0.10.0", + "version": "0.10.4", "license": "MIT", "dependencies": { "@liveviewjs/examples": "*", @@ -20585,7 +20585,7 @@ }, "packages/core": { "name": "liveviewjs", - "version": "0.10.0", + "version": "0.10.4", "license": "MIT", "dependencies": { "deep-object-diff": "^1.1.7", @@ -20672,7 +20672,7 @@ }, "packages/examples": { "name": "@liveviewjs/examples", - "version": "0.10.0", + "version": "0.10.4", "license": "MIT", "dependencies": { "liveviewjs": "*", @@ -20711,7 +20711,7 @@ }, "packages/express": { "name": "@liveviewjs/express", - "version": "0.10.0", + "version": "0.10.4", "license": "MIT", "dependencies": { "express": "^4.17.2", @@ -20757,7 +20757,7 @@ }, "packages/gen": { "name": "@liveviewjs/gen", - "version": "0.10.0", + "version": "0.10.4", "license": "MIT", "dependencies": { "chalk": "^5.2.0", @@ -20770,7 +20770,7 @@ "yargs": "^17.6.2" }, "bin": { - "gen": "dist/cli.js" + "gen": "dist/cli.mjs" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", @@ -24088,7 +24088,7 @@ "chalk": "^5.2.0", "cli-spinners": "^2.7.0", "enquirer": "^2.3.6", - "execa": "*", + "execa": "^6.1.0", "fs-extra": "^11.1.0", "hygen": "^6.2.11", "log-update": "^5.0.1",