docs(htmx_repo): add v2.0.8 changelog entry
This commit is contained in:
583
CHANGELOG.md
Normal file
583
CHANGELOG.md
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [2.0.8] - 2025-10-24
|
||||||
|
|
||||||
|
* [Updated](https://github.com/bigskysoftware/htmx/commit/b9336a96fbdcf28550699971dc2218a90c7a4e01) `parseHTML` to use to use the (unfortunately named) [`Document.parseHTMLUnsafe()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/parseHTMLUnsafe_static)
|
||||||
|
method for better Web Components support
|
||||||
|
* [Added](https://github.com/bigskysoftware/htmx/commit/83a1449a89b1fcd1c1655039ede02d74d61e4800) `pushURL` option to the `htmx.ajax()` API
|
||||||
|
* [Fixed](https://github.com/bigskysoftware/htmx/commit/cd045c3e0eb31776a80e3a4b4c74e37d0631c1f1) `hx-sync` and `htmx:abort` within the Shadow Dom [Issue 3419](https://github.com/bigskysoftware/htmx/issues/3419)
|
||||||
|
* [Fixed](https://github.com/bigskysoftware/htmx/commit/04d6c7249b7fd7b8518ddca92e7a70fdcc651b34) a long-standing bug in history support with respect to relative paths [Issue 3449](https://github.com/bigskysoftware/htmx/issues/3449)
|
||||||
|
* Once again, this is a release mainly done by @MichaelWest22's heroic work, thank you!
|
||||||
|
|
||||||
|
## [2.0.7] - 2025-09-08
|
||||||
|
|
||||||
|
* Fix not preventing link when inside htmx enabled element (fixes https://github.com/bigskysoftware/htmx/issues/3395)
|
||||||
|
* Implement `reportValidity()` for reporting proper form validation errors behind config flag (fixes https://github.com/bigskysoftware/htmx/issues/2372)
|
||||||
|
* Update indicator style to have `visibility:hidden` for screen readers (fixes https://github.com/bigskysoftware/htmx/issues/3354)
|
||||||
|
* Bugfix: swap="outerHTML" on <div> with style attribute leaves htmx-swapping class behind (see https://github.com/bigskysoftware/htmx/pull/3341)
|
||||||
|
|
||||||
|
## [2.0.6] - 2025-06-27
|
||||||
|
|
||||||
|
* [Fix](https://github.com/bigskysoftware/htmx/pull/3357) a [regression](https://github.com/bigskysoftware/htmx/issues/3356)
|
||||||
|
with htmx-powered links that contain other elements in them issuing full page refreshes
|
||||||
|
|
||||||
|
## [2.0.5] - 2025-06-20
|
||||||
|
|
||||||
|
* 100% test coverage! (Thank you @MichaelWest22!)
|
||||||
|
* The default recommended CDN is now jsDelivr
|
||||||
|
* The `inherit` keyword is now supported by `hx-include`, `hx-indicator` and `hx-disabled-elt` to allow you to inherit
|
||||||
|
the value from a parent and extend it.
|
||||||
|
* `hx-on` listeners are now added before processing nodes so events during processing can be captured
|
||||||
|
* Using `<button hx-verb="/endpoint" type="reset">` will now reset the associated form (after submitting to `/endpoint`)
|
||||||
|
* Using `<button formmethod="dialog">` will no longer submit its associated form
|
||||||
|
* Local history cache now uses `sessionStorage` rather than `localStorage` so cross-tab contamination doesn't occur
|
||||||
|
* History restoration now follows the standard swapping code paths
|
||||||
|
* Many other smaller bug and documentation fixes
|
||||||
|
|
||||||
|
## [2.0.4] - 2024-12-13
|
||||||
|
|
||||||
|
* Calling `htmx.ajax` with no target or source now defaults to body (previously did nothing)
|
||||||
|
* Nested [shadow root](https://github.com/bigskysoftware/htmx/commit/5ab508f6523a37890932176f7dc54be9f7a281ff) fix
|
||||||
|
* The `htmx:trigger` event now properly fires on the synthetic `load` event
|
||||||
|
* The synthetic `load` event will not be re-called when an element is reinitialized via `htmx.process()`
|
||||||
|
* Boosted `<form>` tags that issue a `GET` with no or empty `action` attributes will properly replace all existing query
|
||||||
|
parameters
|
||||||
|
* Events that are triggered on htmx-powered elements located outside a form, but that refer to a form via the`form`
|
||||||
|
attribute, now properly cancel the submission of the referred-to form
|
||||||
|
* Extension Updates
|
||||||
|
* `preload` extension was
|
||||||
|
[completely reworked](https://github.com/bigskysoftware/htmx-extensions/commit/fb68dfb48063505d2d7420d717c24ac9a8dae244)
|
||||||
|
by @marisst to be compatible with `hx-boost`, not cause side effect, etc. Thank you!
|
||||||
|
* `response-targets` was updated to not use deprecated methods
|
||||||
|
* A [small fix](https://github.com/bigskysoftware/htmx-extensions/commit/e87e1c3d0bf728b4e43861c7459f3f937883eb99) to
|
||||||
|
`ws` to avoid an error when closing in some cases
|
||||||
|
* The `head-support` extension was updated to work with the `sse` extension
|
||||||
|
|
||||||
|
## [2.0.3] - 2024-10-03
|
||||||
|
* Added support for the experimental `moveBefore()` functionality in [Chrome Canary](https://www.google.com/chrome/canary/),
|
||||||
|
see the [demo page](/examples/move-before) for more information.
|
||||||
|
* Fixed `revealed` event when a resize reveals an element
|
||||||
|
* Enabled `hx-preserve` in oob-swaps
|
||||||
|
* Better degredation of `hx-boost` on forms with query parameters in their `action`
|
||||||
|
* Improved shadowRoot support
|
||||||
|
* Many smaller bug fixes
|
||||||
|
* Moved the core extension documentation back to <https://htmx.org/extensions>
|
||||||
|
|
||||||
|
## [2.0.2] - 2024-08-12
|
||||||
|
* no longer boost forms of type `dialog`
|
||||||
|
* properly trigger the `htmx:trigger` event when an event is delayed or throttled
|
||||||
|
* file upload is now fixed
|
||||||
|
* empty templates that are not used for oob swaps are no longer removed from the DOM
|
||||||
|
* request indicators are not removed when a full page redirect or refresh occurs
|
||||||
|
* elements that have been disabled for a request are properly re-enabled before snapshotting for history
|
||||||
|
* you can now trigger events on other elements using the `HX-Trigger` response header
|
||||||
|
* The `.d.ts` file should now work properly
|
||||||
|
|
||||||
|
## [2.0.1] - 2024-07-12
|
||||||
|
|
||||||
|
* Make the `/dist/htmx.esm.js` file the `main` file in `package.json` to make installing htmx smoother
|
||||||
|
* Update `htmx.d.ts` & include it in the distribution
|
||||||
|
* A fix to avoid removing text-only templates on htmx cleanup
|
||||||
|
* A fix for outerHTML swapping of the `body` tag
|
||||||
|
* Many docs fixes
|
||||||
|
|
||||||
|
## [2.0.0] - 2024-06-17
|
||||||
|
|
||||||
|
* Removed extensions and moved to their own repos linked off of <https://extensions.htmx.org>
|
||||||
|
* The website now supports dark mode! (Thanks [@pokonski](https://github.com/pokonski)!)
|
||||||
|
* The older, deprecated [SSE & WS](https://v1.htmx.org/docs/#websockets-and-sse) attributes were removed
|
||||||
|
* Better support for [Web Components & Shadow DOM](https://htmx.org/examples/web-components/)
|
||||||
|
* HTTP `DELETE` requests now use parameters, rather than form encoded bodies, for their payload (This is in accordance w/ the spec.)
|
||||||
|
* Module support was split into different files:
|
||||||
|
* We now provide specific files in `/dist` for the various JavaScript module styles:
|
||||||
|
* ESM Modules: `/dist/htmx.esm.js`
|
||||||
|
* AMD Modules: `/dist/htmx.amd.js`
|
||||||
|
* CJS Modules: `/dist/htmx.cjs.js`
|
||||||
|
* The `/dist/htmx.js` file continues to be browser-loadable
|
||||||
|
* The `hx-on` attribute, with its special syntax, has been removed in favor of the less-hacky `hx-on:` syntax.
|
||||||
|
* See the [Upgrade Guide](https://htmx.org/migration-guide-htmx-1/) for more details on upgrade steps
|
||||||
|
* The `selectAndSwap()` internal API method was replaced with the public (and much better) [`swap()`](/api/#swap) method
|
||||||
|
|
||||||
|
## [1.9.12] - 2024-04-17
|
||||||
|
|
||||||
|
* [IE Fixes](https://github.com/bigskysoftware/htmx/commit/e64238dba3113c2eabe26b1e9e9ba7fe29ba3010)
|
||||||
|
|
||||||
|
## [1.9.11] - 2024-03-15
|
||||||
|
|
||||||
|
* Fix for new issue w/ web sockets & SSE on iOS 17.4 (thanks apple!)
|
||||||
|
* Fix for double script execution issue when using template parsing
|
||||||
|
* Fix TypeScript types file
|
||||||
|
* Fix SSE Ext: reinstantiate EventSource listeners upon reconnection logic (#2272)
|
||||||
|
|
||||||
|
## [1.9.10] - 2023-12-21
|
||||||
|
|
||||||
|
* `hx-on*` attributes now support the form `hx-on-`, with a trailing dash, to better support template systems (such as EJS)
|
||||||
|
that do not like double colons in HTML attributes.
|
||||||
|
* Added an `htmx.config.triggerSpecsCache` configuration property that can be set to an object to cache the trigger spec parsing
|
||||||
|
* Added a `path-params.js` extension for populating request paths with variable values
|
||||||
|
* Many smaller bug fixes & improvements
|
||||||
|
|
||||||
|
## [1.9.9] - 2023-11-21
|
||||||
|
|
||||||
|
* Allow CSS selectors with whitespace in attributes like `hx-target` by using parens or curly-braces
|
||||||
|
* Properly allow users to override the `Content-Type` request header
|
||||||
|
* Added the `select` option to `htmx.ajax()`
|
||||||
|
* Fixed a race condition in readystate detection that lead to htmx not being initialized in some scenarios with 3rd
|
||||||
|
party script loaders
|
||||||
|
* Fixed a bug that caused relative resources to resolve against the wrong base URL when a new URL is pushed
|
||||||
|
* Fixed a UI issue that could cause indicators to briefly flash
|
||||||
|
|
||||||
|
## [1.9.8] - 2023-11-06
|
||||||
|
|
||||||
|
* Fixed a few npm & build related issues
|
||||||
|
|
||||||
|
## [1.9.7] - 2023-11-03
|
||||||
|
|
||||||
|
* Fixed a bug where a button associated with a form that is swapped out of the DOM caused errors
|
||||||
|
* The `hx-target-error` attribute was added to the `response-targets` extension, allowing you to capture all 400 & 500
|
||||||
|
responses with a single attribute
|
||||||
|
* `hx-on` now properly supports multiple listeners
|
||||||
|
* The `hx-confirm` prompt is now passed into custom confirmation handlers
|
||||||
|
* `next` and `previous` are now valid _extended CSS_ symbols in htmx
|
||||||
|
* The `htmx:beforeHistoryUpdate` event was added
|
||||||
|
* Properly ignore the `dialog` formmethod on buttons when resolving the HTTP method to use
|
||||||
|
* Added a `htmx.config.scrollIntoViewOnBoost` option that may be set to `false` to disable scrolling the top of the
|
||||||
|
body into view for boosted elements
|
||||||
|
|
||||||
|
## [1.9.6] - 2023-09-22
|
||||||
|
|
||||||
|
* IE support has been restored (thank you @telroshan!)
|
||||||
|
* Introduced the `hx-disabled-elt` attribute to allow specifying elements to disable during a request
|
||||||
|
* You can now explicitly decide to ignore `title` tags found in new content via the `ignoreTitle` option in `hx-swap` and the `htmx.config.ignoreTitle` configuration variable.
|
||||||
|
* `hx-swap` modifiers may be used without explicitly specifying the swap mechanism
|
||||||
|
* Arrays are now supported in the `client-side-templates` extension
|
||||||
|
* XSLT support in the `client-side-templates` extension
|
||||||
|
* Support `preventDefault()` in extension event handling
|
||||||
|
* Allow the `HX-Refresh` header to apply even after an `HX-Redirect` has occurred
|
||||||
|
* the `formaction` and `formmethod` attributes on buttons are now properly respected
|
||||||
|
* `hx-on` can now handle events with dots in their name
|
||||||
|
* `htmx.ajax()` now always returns a Promise
|
||||||
|
* Handle leading `style` tag parsing more effectively
|
||||||
|
|
||||||
|
## [1.9.5] - 2023-08-25
|
||||||
|
|
||||||
|
* Web sockets now properly pass the target id in the HEADERS struct
|
||||||
|
* A very rare loading state bug was fixed (see https://github.com/bigskysoftware/htmx/commit/93bd81b6d003bb7bc445f10192bdb8089fa3495d)
|
||||||
|
* `hx-on` will not evaluate if `allowEval` is set to false
|
||||||
|
* You can disable the interpretation of script tags with the new `htmx.config.allowScriptTags` config variable
|
||||||
|
* You can now disable htmx-based requests to non-origin hosts via the `htmx.config.selfRequestsOnly` config variable
|
||||||
|
* The [Security](https://htmx.org/docs#security) section has been expanded to help developers better understand how to
|
||||||
|
properly secure their htmx-based applications.
|
||||||
|
|
||||||
|
## [1.9.4] - 2023-07-25
|
||||||
|
|
||||||
|
* This is a bug-fix release for the most part, w/a heavy dose of @telroshan
|
||||||
|
* The `HX-Trigger` response header now supports comma separated event names
|
||||||
|
* Submit buttons that use the `form` attribute now work correctly
|
||||||
|
* The `changed` modifier now uses the triggering element, rather than the element the `hx-trigger` is defined on
|
||||||
|
* `hx-disable` is now handled dynamically so it can be added and removed
|
||||||
|
* IE11 compatibility restored! (maybe, hard to test)
|
||||||
|
* Fixed bug with `hx-on` event handler cleanup
|
||||||
|
* Many smaller bug fixes, typo fixes and general improvements
|
||||||
|
|
||||||
|
## [1.9.3] - 2023-07-14
|
||||||
|
|
||||||
|
* The `hx-on` attribute has been deprecated (sorry) in favor of `hx-on:<event name>` attributes. See [`hx-on`](/attributes/hx-on) for more information.
|
||||||
|
* We now have functioning CI using GitHub actions!
|
||||||
|
* You can now configure if a type of HTTP request uses the body for parameters or not. In particular, the `DELETE` _should_ use
|
||||||
|
query parameters, according to the spec. htmx has used the body, instead. To avoid breaking code we are keeping this undefined
|
||||||
|
behavior for now, but allowing people to fix it for their use cases by updating the `htmx.config.methodsThatUseUrlParams` config
|
||||||
|
option. Thank you to Alex and Vincent for their feedback and work on this issue!
|
||||||
|
* The `this` symbol is now available in event filter expressions, and refers to the element the `hx-trigger` is on
|
||||||
|
* Fix bug where the `htmx:afterSettle` event was raised multiple times with oob swaps occurred
|
||||||
|
* A large number of accessibility fixes were made in the docs (Thank you Denis & crew!)
|
||||||
|
* Fixed bug w/ WebSocket extension initialization caused by "naked" `hx-trigger` feature
|
||||||
|
* The `HX-Reselect` HTTP response header has been added to change the selection from the returned content
|
||||||
|
* Many other smaller bug fixes
|
||||||
|
|
||||||
|
## [1.9.2] - 2023-04-28
|
||||||
|
|
||||||
|
* Fixed bug w/ `hx-on` not properly de-initializing
|
||||||
|
|
||||||
|
## [1.9.1] - 2023-04-27
|
||||||
|
|
||||||
|
* Fixed a bug with the new naked triggers that prevented boosted elements with explicit `hx-trigger`'s from functioning
|
||||||
|
properly
|
||||||
|
* Added code to play well with other libraries that also use the `window.onpopstate` Daily reminder: <https://htmx.org/img/memes/javascripthistory.png>
|
||||||
|
|
||||||
|
## [1.9.0] - 2023-04-11
|
||||||
|
|
||||||
|
* Support for generalized inline event handling via the new [`hx-on`](/attributes/hx-on) attribute, which addresses
|
||||||
|
the shortcoming of limited [`onevent` properties](https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers#using_onevent_properties) attributes in HTML.
|
||||||
|
* Support for [view transitions](/docs#view-transitions), based on the experimental [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
|
||||||
|
currently available in Chrome 111+ and coming to other browsers soon.
|
||||||
|
* Support for "naked" [`hx-trigger`](/attributes/hx-trigger) attributes, where an `hx-trigger` is present on an element
|
||||||
|
that does not have an `hx-get`, etc. defined on it. Instead, it will trigger the new `htmx:triggered` event, which can
|
||||||
|
be responded to via your [preferred scripting solution](/docs#scripting).
|
||||||
|
* A memory leak fix by [@croxton](https://github.com/bigskysoftware/htmx/commit/8cd3a480a7388877628ce8b9b8e50cd5df48bb81)
|
||||||
|
* The htmx website has been migrated from 11ty to [zola](https://www.getzola.org/) by [@danieljsummers](https://github.com/danieljsummers), cutting
|
||||||
|
way down on the number of "development" javascript dependencies
|
||||||
|
* Many other smaller bug fixes
|
||||||
|
|
||||||
|
## [1.8.6] - 2023-03-02
|
||||||
|
|
||||||
|
* ESM support!
|
||||||
|
* Sass has been vanquished from the htmx.org website, which should set us up for some good progress going forward
|
||||||
|
* Fixed a bug where the `changed` modifier on `keyup` did not work properly if an input was tabbed into
|
||||||
|
* Many other smaller bug fixes and doc fixes
|
||||||
|
|
||||||
|
## [1.8.5] - 2023-01-17
|
||||||
|
|
||||||
|
* Support a new optional cache-busting configuration option, `getCacheBusterParam`, to allow browsers to disambiguate
|
||||||
|
between `GET` requests from htmx and from the raw browser
|
||||||
|
* Support new `hx-history='false'` attribute, to prevent sensitive data from being stored in the history cache. (Thank you @croxton!)
|
||||||
|
* Extensive new event-oriented features are available in the [Web Socket](/extensions/web-sockets/) extension (Thank you @Renerick!)
|
||||||
|
* A bug fix for when a form contains multiple empty input values with the same name (Thank you @bluekeyes!)
|
||||||
|
* A bug fix around inputs that throw exceptions when calling `setSelectionRange()` (Thank you @gone!)
|
||||||
|
* A bug fix to pass through the proper event for the `htmx:configRequest` event
|
||||||
|
* A bug fix/improvement for the `preload` extension
|
||||||
|
* Many other small bug fixes
|
||||||
|
|
||||||
|
## [1.8.4] - 2022-11-05
|
||||||
|
|
||||||
|
* Fix the _exact same_ regression in `revealed` logic as in 1.8.2
|
||||||
|
|
||||||
|
## [1.8.3] - 2022-11-04
|
||||||
|
|
||||||
|
* A new [`htmx:confirm` event](/events#htmx:confirm) was added that allows for asynchronous confirmation dialogs to
|
||||||
|
be integrated into htmx requests
|
||||||
|
* The new [head-support](/extensions/head-support) extension allows for more elaborate head tag merging than standard htmx
|
||||||
|
supports. This functionality may be integrated into htmx 2.0, depending on feedback.
|
||||||
|
* The new [multi-swap](/extensions/multi-swap) provides more elaborate swapping of multiple elements on a screen using
|
||||||
|
a custom swap strategy
|
||||||
|
* Many doc fixes (thank you to everyone who contributed!)
|
||||||
|
|
||||||
|
## [1.8.2] - 2022-10-12
|
||||||
|
|
||||||
|
* Fix regression in `revealed` logic
|
||||||
|
|
||||||
|
## [1.8.1] - 2022-10-11
|
||||||
|
|
||||||
|
* We now keep a count of outstanding requests for an indicator, so more than one overlapping request can share the same
|
||||||
|
indicator without issues
|
||||||
|
* We now track the attribute state of an element and re-initialize it if `htmx.process()` is called on the element and
|
||||||
|
the attributes have changed
|
||||||
|
* [Idiomorph](https://github.com/bigskysoftware/idiomorph) is now available for all your morph-swapping needs
|
||||||
|
* The `unset` directive now works properly for `hx-vals` and `hx-vars`
|
||||||
|
* The title of the page is now properly set on a history cache miss
|
||||||
|
* The new [`hx-validate`](https://htmx.org/attributes/hx-validate) attribute will force elements to validate before a request, even if
|
||||||
|
they are not within a form being submitted
|
||||||
|
* Many smaller bug and docs fixes
|
||||||
|
|
||||||
|
## [1.8.0] - 2022-7-12
|
||||||
|
|
||||||
|
* **NOTE**: This release involved some changes to touchy code (e.g. history support) so please test thoroughly and let
|
||||||
|
us know if you see any issues
|
||||||
|
* Boosted forms now will automatically push URLs into history as with links. The [response URL](https://caniuse.com/mdn-api_xmlhttprequest_responseurl)
|
||||||
|
detection API support is good enough that we feel comfortable making this the default now.
|
||||||
|
* If you do not want this behavior you can add `hx-push-url='false'` to your boosted forms
|
||||||
|
* The [`hx-replace-url`](https://htmx.org/attributes/hx-replace-url) attribute was introduced, allowing you to replace
|
||||||
|
the current URL in history (to complement `hx-push-url`)
|
||||||
|
* Bug fix - if htmx is included in a page more than once, we do not process elements multiple times
|
||||||
|
* Bug fix - When localStorage is not available we do not attempt to save history in it
|
||||||
|
* [Bug fix](https://github.com/bigskysoftware/htmx/issues/908) - `hx-boost` respects the `enctype` attribute
|
||||||
|
* `m` is now a valid timing modifier (e.g. `hx-trigger="every 2m"`)
|
||||||
|
* `next` and `previous` are now valid extended query selector modifiers, e.g. `hx-target="next div"` will target the
|
||||||
|
next div from the current element
|
||||||
|
* Bug fix - `hx-boost` will boost anchor tags with a `_self` target
|
||||||
|
* The `load` event now properly supports event filters
|
||||||
|
* The websocket extension has had many improvements: (A huge thank you to Denis Palashevskii, our newest committer on the project!)
|
||||||
|
* Implement proper `hx-trigger` support
|
||||||
|
* Expose trigger handling API to extensions
|
||||||
|
* Implement safe message sending with sending queue
|
||||||
|
* Fix `ws-send` attributes connecting in new elements
|
||||||
|
* Fix OOB swapping of multiple elements in response
|
||||||
|
* The `HX-Location` response header now implements client-side redirects entirely within htmx
|
||||||
|
* The `HX-Reswap` response header allows you to change the swap behavior of htmx
|
||||||
|
* The new [`hx-select-oob`](https://htmx.org/attributes/hx-select-oob) attribute selects one or more elements from a server response to swap in via an out of band swap
|
||||||
|
* The new [`hx-replace-url`](https://htmx.org/attributes/hx-replace-url) attribute can be used to replace the current URL in the location
|
||||||
|
bar (very similar to `hx-push-url` but no new history entry is created). The corresponding `HX-Replace-Url` response header can be used as well.
|
||||||
|
* htmx now properly handles anchors in both boosted links, as well as in `hx-get`, etc. attributes
|
||||||
|
|
||||||
|
## [1.7.0] - 2022-02-22
|
||||||
|
|
||||||
|
* The new [`hx-sync`](https://htmx.org/attributes/hx-sync) attribute allows you to synchronize multiple element requests on a single
|
||||||
|
element using various strategies (e.g. replace)
|
||||||
|
* You can also now abort an element making a request by sending it the `htmx:abort` event
|
||||||
|
* [Server Sent Events](/extensions/server-sent-events) and [Web Sockets](/extensions/web-sockets) are now available as
|
||||||
|
extensions, in addition to the normal core support. In htmx 2.0, the current `hx-sse` and `hx-ws` attributes will be
|
||||||
|
moved entirely out to these new extensions. By moving these features to extensions we will be able to add functionality
|
||||||
|
to both of them without compromising the core file size of htmx. You are encouraged to move over to the new
|
||||||
|
extensions, but `hx-sse` and `hx-ws` will continue to work indefinitely in htmx 1.x.
|
||||||
|
* You can now mask out [attribute inheritance](/docs#inheritance) via the [`hx-disinherit`](https://htmx.org/attributes/hx-disinherit) attribute.
|
||||||
|
* The `HX-Push` header can now have the `false` value, which will prevent a history snapshot from occurring.
|
||||||
|
* Many new extensions, with a big thanks to all the contributors!
|
||||||
|
* A new [`alpine-morph`](/extensions/alpine-morph) allows you to use Alpine's swapping engine, which preserves Alpine
|
||||||
|
* A [restored](/extensions/restored) extension was added that will trigger a `restore` event on all elements in the DOM
|
||||||
|
on history restoration.
|
||||||
|
* A [loading-states](/extensions/loading-states) extension was added that allows you to easily manage loading states
|
||||||
|
while a request is in flight, including disabling elements, and adding and removing CSS classes.
|
||||||
|
* The `this` symbol now resolves properly for the [`hx-include`](https://htmx.org/attributes/hx-include) and [`hx-indicator`](https://htmx.org/attributes/hx-indicator)
|
||||||
|
attributes
|
||||||
|
* When an object is included via the [`hx-vals`](https://htmx.org/attributes/hx-vals) attribute, it will be converted to JSON (rather
|
||||||
|
than rendering as the string `[Object object]"`)
|
||||||
|
* You can now pass a swap style in to the `htmx.ajax()` function call.
|
||||||
|
* Poll events now contain a `target` attribute, allowing you to filter a poll on the element that is polling.
|
||||||
|
* Two new Out Of Band-related events were added: `htmx:oobBeforeSwap` & `htmx:oobAfterSwap`
|
||||||
|
|
||||||
|
## [1.6.1] - 2021-11-22
|
||||||
|
|
||||||
|
* A new `HX-Retarget` header allows you to change the default target of returned content
|
||||||
|
* The `htmx:beforeSwap` event now includes another configurable property: `detail.isError` which can
|
||||||
|
be used to indicate if a given response should be treated as an error or not
|
||||||
|
* The `htmx:afterRequest` event has two new detail properties: `success` and `failed`, allowing you to write
|
||||||
|
trigger filters in htmx or hyperscript:
|
||||||
|
```applescript
|
||||||
|
on htmx:afterRequest[failed]
|
||||||
|
set #myCheckbox's checked to true
|
||||||
|
```
|
||||||
|
* Fixed the `from:` option in [`hx-trigger`](https://htmx.org/attributes/hx-trigger) to support `closest <CSS selector>`
|
||||||
|
and `find <CSS selector>` forms
|
||||||
|
* Don't boost anchor tags with an explicit `target` set
|
||||||
|
* Don't cancel all events on boosted elements, only the events that naturally trigger them (click for anchors, submit
|
||||||
|
for forms)
|
||||||
|
* Persist revealed state in the DOM so that on history navigation, revealed elements are not re-requested
|
||||||
|
* Process all [`hx-ext`](https://htmx.org/attributes/hx-ext) attributes, even if no other htmx attribute is on the element
|
||||||
|
* Snapshot the current URL on load so that history support works properly after a page refresh occurs
|
||||||
|
* Many, many documentation updates (thank you to all the contributors!)
|
||||||
|
|
||||||
|
|
||||||
|
## [1.6.0] - 2021-10-01
|
||||||
|
|
||||||
|
* Completely reworked `<script>` tag support that now supports the `<script src="...'/>` form
|
||||||
|
* You can now use the value `unset` to clear a property that would normally be inherited (e.g. hx-confirm)
|
||||||
|
* The `htmx-added` class is added to new content before a swap and removed after the settle phase, which allows you
|
||||||
|
more flexibility in writing CSS transitions for added content (rather than relying on the target, as with `htmx-settling`)
|
||||||
|
* The `htmx:beforeSwap` event has been updated to allow you to [configure swapping](https://htmx.org/docs/#modifying_swapping_behavior_with_events)
|
||||||
|
behavior
|
||||||
|
* Improved `<title>` extraction support
|
||||||
|
* You can listen to events on the `window` object using the `from:` modifier in `hx-trigger`
|
||||||
|
* The `root` option of the `intersect` event was fixed
|
||||||
|
* Boosted forms respect the `enctype` declaration
|
||||||
|
* The `HX-Boosted` header will be sent on requests from boosted elements
|
||||||
|
* Promises are not returned from the main ajax function unless it is an api call (i.e. `htmx.ajax`)
|
||||||
|
|
||||||
|
## [1.5.0] - 2021-7-12
|
||||||
|
|
||||||
|
* Support tracking of button clicked during a form submission
|
||||||
|
* Conditional polling via the [hx-trigger](https://htmx.org/attributes/hx-trigger) attribute
|
||||||
|
* `document` is now a valid pseudo-selector on the [hx-trigger](https://htmx.org/attributes/hx-trigger) `from:` argument, allowing you
|
||||||
|
to listen for events on the document.
|
||||||
|
* Added the [hx-request](https://htmx.org/attributes/hx-request) attribute, allowing you to configure the following aspects of the request
|
||||||
|
* `timeout` - the timeout of the request
|
||||||
|
* `credentials` - if the request will send credentials
|
||||||
|
* `noHeaders` - strips all headers from the request
|
||||||
|
* Along with the above attribute, you can configure the default values for each of these via the corresponding `htmx.config`
|
||||||
|
properties (e.g. `htmx.config.timeout`)
|
||||||
|
* Both the `scroll` and `show` options on [hx-swap](https://htmx.org/attributes/hx-swap) now support extended syntax for selecting the
|
||||||
|
element to scroll or to show, including the pseudo-selectors `window:top` and `window:bottom`.
|
||||||
|
|
||||||
|
## [1.4.1] - 2021-6-1
|
||||||
|
|
||||||
|
* typo fix
|
||||||
|
|
||||||
|
## [1.4.0] - 2021-5-25
|
||||||
|
|
||||||
|
* Added the `queue` option to the [hx-trigger](https://htmx.org/attributes/hx-trigger) attribute, allowing you to specify how events
|
||||||
|
should be queued when they are received with a request in flight
|
||||||
|
* The `htmx.config.useTemplateFragments` option was added, allowing you to use HTML template tags for parsing content
|
||||||
|
from the server. This allows you to use Out of Band content when returning things like table rows, but it is not
|
||||||
|
IE11 compatible.
|
||||||
|
* The `defaultSettleDelay` was dropped to 20ms from 100ms
|
||||||
|
* Introduced a new synthetic event, [intersect](https://htmx.org/docs#pecial-events) that allows you to trigger when an item is scrolled into view
|
||||||
|
as specified by the `IntersectionObserver` API
|
||||||
|
* Fixed timing issue that caused exceptions in the `reveal` logic when scrolling at incredible speeds - <https://github.com/bigskysoftware/htmx/issues/463>
|
||||||
|
* Fixed bug causing SVG titles to be incorrectly used as page title - <https://github.com/bigskysoftware/htmx/issues/459>
|
||||||
|
* Boosted forms that issue a GET will now push the URL by default - <https://github.com/bigskysoftware/htmx/issues/485>
|
||||||
|
* Better dispatch of request events when an element is removed from the DOM
|
||||||
|
* Fixed a bug causing `hx-prompt` to fail
|
||||||
|
* The `htmx.config.withCredentials` option was added, to send credentials with ajax requests (default is `false`)
|
||||||
|
* The `throttle` option on `hx-trigger` does not delay the initial request any longer
|
||||||
|
* The `meta` key is ignored on boosted links
|
||||||
|
* `<script>` tags are now evaluated in the global scope
|
||||||
|
* `hx-swap` now supports the `none` option
|
||||||
|
* Safari text selection bug - <https://github.com/bigskysoftware/htmx/issues/438>
|
||||||
|
|
||||||
|
## [1.3.3] - 2021-4-5
|
||||||
|
|
||||||
|
* Added the [`hx-disabled`](https://htmx.org/docs#security) attribute to allow htmx to be turned off for parts of the DOM
|
||||||
|
* SSE now uses a full-jitter exponential backoff algorithm on reconnection, using the `htmx.config.wsReconnectDelay`
|
||||||
|
setting
|
||||||
|
|
||||||
|
## [1.3.2] - 2021-3-9
|
||||||
|
|
||||||
|
* Bug fixes
|
||||||
|
|
||||||
|
## [1.3.1] - 2021-3-9
|
||||||
|
|
||||||
|
* IE11 fixes
|
||||||
|
|
||||||
|
## [1.3.0] - 2021-3-6
|
||||||
|
|
||||||
|
* Support a `target` modifier on `hx-trigger` to filter based on the element targeted by an event. This allows
|
||||||
|
lazy binding to that target selector.
|
||||||
|
* Events are no longer consumed by the first element that might handle them, unless the `consume` keyword is
|
||||||
|
added to the `hx-trigger` specification
|
||||||
|
* Added the `htmx:beforeSend` event, fired just before an ajax request begins
|
||||||
|
* SSE swaps are properly settled
|
||||||
|
* Fixed bug that was improperly cancelling all clicks on anchors
|
||||||
|
* `htmx.ajax()` now returns a promise
|
||||||
|
|
||||||
|
## [1.2.1] - 2021-2-19
|
||||||
|
|
||||||
|
* Fixed an issue with the history cache, where the cache was getting blown out after the first navigation backwards
|
||||||
|
* Added the `htmx.config.refreshOnHistoryMiss` option, allowing users to trigger a full page refresh on history cache miss
|
||||||
|
rather than issuing an AJAX request
|
||||||
|
|
||||||
|
## [1.2.0] - 2021-2-13
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
* `hx-vars` has been deprecated in favor of `hx-vals`
|
||||||
|
* `hx-vals` now supports a `javascript:` prefix to achieve the behavior that `hx-vars` provided
|
||||||
|
* The new `hx-headers` attribute allows you to add headers to a request via an attribute. Like `hx-vals` it supports
|
||||||
|
JSON or javascript via the `javascript:` prefix
|
||||||
|
* `hx-include` will now include all inputs under an element, even if that element is not a form tag
|
||||||
|
* The [preload extension](https://htmx.org/extensions/preload/) now offers a `preload-images="true"` attribute that will aggressively load images in preloaded content
|
||||||
|
* On requests driven by a history cache miss, the new `HX-History-Restore-Request` header is included so that the server
|
||||||
|
can differentiate between history requests and normal requests
|
||||||
|
|
||||||
|
### Improvements & Bug fixes
|
||||||
|
|
||||||
|
* Improved handling of precedence of input values to favor the enclosing form (see [here](https://github.com/bigskysoftware/htmx/commit/a10e43d619dc340aa324d37772c06a69a2f47ec9))
|
||||||
|
* Moved event filtering logic *after* `preventDefault` so filtering still allows events to be properly handled
|
||||||
|
* No longer trigger after swap events on elements that have been removed via an `outerHTML` swap
|
||||||
|
* Properly remove event handlers added to other elements when an element is removed from the DOM
|
||||||
|
* Handle the `scroll:` modifier in `hx-swap` properly when an `outerHTML` swap occurs
|
||||||
|
* Lots of docs fixes
|
||||||
|
|
||||||
|
## [1.1.0] - 2021-1-6
|
||||||
|
|
||||||
|
* Newly added [preload extension](https://htmx.org/extensions/preload/) allows you to preload resources for lower
|
||||||
|
latency requests!
|
||||||
|
* Support the `ignore:` modifier for extensions
|
||||||
|
* Updated form variable order inclusion to include the enclosing form *last* so that, in the presence of multiple
|
||||||
|
values, the most relevant value is the most likely to be selected by the server
|
||||||
|
* Support for the [`htmx.ajax()`](https://dev.htmx.org/api/#ajax) javascript function, to issue an htmx-style ajax
|
||||||
|
request from javascript
|
||||||
|
* Removed the following htmx request headers for better cache behavior: `HX-Event-Target`, `HX-Active-Element`,
|
||||||
|
`HX-Active-Element-Name`, `HX-Active-Element-Value`
|
||||||
|
* Added the [`hx-preserve`](https://dev.htmx.org/attributes/hx-preserve) attribute, which allows
|
||||||
|
you to preserve elements across requests (for example, to keep a video element playing properly)
|
||||||
|
* The [path-deps](https://dev.htmx.org/extensions/path-deps/#refresh) now surfaces a small api
|
||||||
|
for refreshing path dependencies manually in javascript
|
||||||
|
* Now support the `from:` clause on [`hx-trigger`](https://dev.htmx.org/attributes/hx-trigger) to
|
||||||
|
allow an element to respond to events on other elements.
|
||||||
|
* Added the `htmx:beforeProcessNode` event, renamed the (previously undocumented) `htmx:processedNode` to `htmx:afterProcessNode`
|
||||||
|
* Added `closest` syntax support for the [`hx-indicator`](https://dev.htmx.org/attributes/hx-indicator) attribute
|
||||||
|
* Added `on load` support for the newest version of [hyperscript](https://hyperscript.org)
|
||||||
|
* Added the `htmx.config.allowEval` configuration value, for CSP compatibility
|
||||||
|
* Bug fixes & improvements
|
||||||
|
|
||||||
|
## [1.0.2] - 2020-12-12
|
||||||
|
|
||||||
|
* Extend all API methods to take a string selector as well as an element
|
||||||
|
* Out of band swap elements need not be top level now
|
||||||
|
* [`hx-swap-oob`](https://htmx.org/attributes/hx-swap-oob) now can accept a CSS selector to retarget with
|
||||||
|
|
||||||
|
## [1.0.1] - 2020-12-04
|
||||||
|
|
||||||
|
* AJAX file upload now correctly fires events, allowing for [a proper progress bar](https://htmx.org/examples/file-upload)
|
||||||
|
* htmx api functions that expect an element now can accept a string selector instead:
|
||||||
|
```js
|
||||||
|
htmx.on('#form', 'htmx:xhr:progress', function(evt) {
|
||||||
|
htmx.find('#progress').setAttribute('value', evt.detail.loaded/evt.detail.total * 100)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
* htmx now properly handles the `multiple` attribute on `<select>` elements
|
||||||
|
|
||||||
|
## [1.0.0] - 2020-11-24
|
||||||
|
|
||||||
|
* Bumped the release version :)
|
||||||
|
|
||||||
|
## [0.4.1] - 2020-11-23
|
||||||
|
|
||||||
|
* Fixed bug with title tag support when title tag contained HTML entities
|
||||||
|
* Pass properties for the `loadstart`, `loadend`, `progress`, `abort` events through properly to the htmx equivalents
|
||||||
|
|
||||||
|
## [0.4.0] - 2020-11-16
|
||||||
|
|
||||||
|
* Now support the `HX-Redirect` and `HX-Refresh` response headers for redirecting client side and triggering a page refresh, respectively
|
||||||
|
* `hx-vars` now overrides input values
|
||||||
|
* `<title>` tags in responses will be used to update page titles
|
||||||
|
* All uses of `eval()` have been removed in favor of `Function`
|
||||||
|
* [`hx-vals`](https://htmx.org/attributes/hx-vals) is available as a safe alternative to `hx-vars`. It uses `JSON.parse()` rather than evaluation, if you wish to safely pass user-provided values through to htmx.
|
||||||
|
|
||||||
|
## [0.3.0] - 2020-10-27
|
||||||
|
|
||||||
|
* `hx-trigger` parsing has been rewritten and now supports [trigger filters](https://htmx.org/docs/#trigger-filters) to filter
|
||||||
|
events based on arbitrary javascript expressions
|
||||||
|
* htmx now supports two additional response headers `HX-Trigger-After-Swap` and `HX-Trigger-After-Settle` allowing
|
||||||
|
an event to be triggered after a given life cycle event (instead of before the swap)
|
||||||
|
* The `requestConfig` is now passed out to events surrounding the AJAX life cycle
|
||||||
|
* htmx now evaluates `<script>` tags as javascript when no language is defined on them
|
||||||
|
* A new [`event-header`](https://htmx.org/extensions/event-header) extension, which will include a serialized JSON representation of the triggering event in requests
|
||||||
|
|
||||||
|
## [0.2.0] - 2020-9-30
|
||||||
|
|
||||||
|
* AJAX file upload [support](https://htmx.org/docs#files)
|
||||||
|
* The HTML validation API is [respected](https://htmx.org/docs#validation)
|
||||||
|
|
||||||
|
## [0.1.0] - 2020-9-18
|
||||||
|
|
||||||
|
* *BREAKING CHANGE*: The SSE attribute [`hx-sse`](https://htmx.org/attributes/hx-sse/) and the Web Sockets attribute [`hx-ws`](https://htmx.org/attributes/hx-ws) have changed syntax to now use colon separators: `hx-sse='connect:/chat swap:message'`
|
||||||
|
* The SSE attribute [`hx-sse`](https://htmx.org/attributes/hx-sse/) allows for swapping content directly on an event, in addition to triggering an htmx element,
|
||||||
|
with the new `swap:<event name>` syntax.
|
||||||
|
* [`hx-target`](https://htmx.org/attributes/hx-target) now supports a `find` syntax to find elements below the element by a CSS selector
|
||||||
|
* htmx plays better with deferred loading and many package managers
|
||||||
|
* All htmx events are dispatched in both camelCase as well as kebab-case, for better compatibility with AlpineJS and other frameworks. (e.g. `htmx:afterOnLoad` will also be triggered as
|
||||||
|
`htmx:after-on-load`)
|
||||||
|
* [hypeerscript](https://hyperscript.org) is now initialized independently of htmx
|
||||||
|
|
||||||
|
## [0.0.8] - 2020-7-8
|
||||||
|
|
||||||
|
* The `view` modifier on `hx-swap` has been renamed to `show`: `hx-swap='innerHTML show:top'`
|
||||||
|
|
||||||
|
## [0.0.7] - 2020-6-30
|
||||||
|
|
||||||
|
* The [`hx-swap`](https://htmx.org/attributes/hx-swap) attribute now supports two new modifiers:
|
||||||
|
* `scroll` - allows you to scroll the target to the `top` or `bottom`
|
||||||
|
* `view` - allows you to scroll the `top` or `bottom` of the target into view
|
||||||
|
* The [`hx-push-url`](https://htmx.org/attributes/hx-push-url) attribute now can optionally take a URL to push, in addition to `true` and `false`
|
||||||
|
* Added the [`hx-vars`](https://htmx.org/attributes/hx-vars) attribute that allows you to dynamically add to the parameters that will be submitted with a request
|
||||||
|
|
||||||
|
## [0.0.6] - 2020-6-20
|
||||||
|
|
||||||
|
* Custom request/response headers no longer start with the `X-` prefix, which is no longer recommended
|
||||||
|
* empty verb attributes are now allowed and follow the anchor tag semantics (e.g. `<div hx-get></div>`)
|
||||||
|
* nunjuks inline rendering is now supported in the `client-side-templates` extension
|
||||||
|
* the new `ajax-header` extension includes the `X-Requested-With` header
|
||||||
|
* bad JSON is now handled more gracefully
|
||||||
|
* `hx-swap="none"` will cause no swap to take place <https://github.com/bigskysoftware/htmx/issues/89>
|
||||||
|
* `hx-trigger` now supports a `throttle` modifier <https://github.com/bigskysoftware/htmx/issues/88>
|
||||||
|
* the focused element is preserved if possible after a replacement
|
||||||
|
* perf improvements for large DOM trees with sparse `hx-` annotations
|
||||||
|
|
||||||
|
## [0.0.4] - 2020-5-24
|
||||||
|
|
||||||
|
* Extension mechanism added
|
||||||
|
* SSE support added
|
||||||
|
* WebSocket support added
|
||||||
|
|
||||||
|
## [0.0.3] - 2020-5-17
|
||||||
|
|
||||||
|
* Renamed to htmx
|
||||||
|
* A bug fix for the `hx-prompt` attribute
|
||||||
|
* A bug fix for multiple `hx-swap-oob` attributes
|
||||||
|
* Moved the default CSS indicator injection into its own sheet to avoid breaking
|
||||||
|
* Added the `htmx.config.includeIndicatorStyles` configuration option so people can opt out of injecting the indicator CSS
|
||||||
|
|
||||||
|
|
||||||
|
## [0.0.1] - 2020-5-15
|
||||||
|
|
||||||
|
* Initial release (originally named kutty)
|
||||||
140
components/stepper.templ
Normal file
140
components/stepper.templ
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// StepInfo represents a single step in the stepper
|
||||||
|
type StepInfo struct {
|
||||||
|
Number int
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnboardingStepper renders a 3-step progress indicator
|
||||||
|
// currentStep: 1-indexed current step number
|
||||||
|
// steps: slice of step labels (e.g., []string{"Welcome", "Learn", "Get Started"})
|
||||||
|
templ OnboardingStepper(currentStep int, steps []string) {
|
||||||
|
@stepperStyles()
|
||||||
|
<div class="onboarding-stepper">
|
||||||
|
for i, label := range steps {
|
||||||
|
@stepperItem(i+1, label, currentStep)
|
||||||
|
if i < len(steps)-1 {
|
||||||
|
@stepperLine(i+1 < currentStep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// stepperItem renders a single step circle with label
|
||||||
|
templ stepperItem(stepNum int, label string, currentStep int) {
|
||||||
|
<div class={ "stepper-item", templ.KV("active", stepNum == currentStep), templ.KV("completed", stepNum < currentStep) } data-step={ strconv.Itoa(stepNum) }>
|
||||||
|
<div class="stepper-number">
|
||||||
|
if stepNum < currentStep {
|
||||||
|
<wa-icon name="check" style="font-size: 14px;"></wa-icon>
|
||||||
|
} else {
|
||||||
|
{ strconv.Itoa(stepNum) }
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<span class="stepper-label">{ label }</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// stepperLine renders the connecting line between steps
|
||||||
|
templ stepperLine(completed bool) {
|
||||||
|
<div class={ "stepper-line", templ.KV("completed", completed) }></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressDots renders a compact dot-based progress indicator
|
||||||
|
templ ProgressDots(current int, total int) {
|
||||||
|
<div class="progress-dots">
|
||||||
|
for i := 1; i <= total; i++ {
|
||||||
|
<div class={ "progress-dot", templ.KV("active", i == current), templ.KV("completed", i < current) }></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ stepperStyles() {
|
||||||
|
<style>
|
||||||
|
.onboarding-stepper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--wa-space-s);
|
||||||
|
padding: var(--wa-space-m) 0;
|
||||||
|
}
|
||||||
|
.stepper-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--wa-space-xs);
|
||||||
|
}
|
||||||
|
.stepper-number {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: var(--wa-font-size-s);
|
||||||
|
font-weight: 600;
|
||||||
|
background: var(--wa-color-neutral-200);
|
||||||
|
color: var(--wa-color-neutral-600);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.stepper-item.active .stepper-number {
|
||||||
|
background: var(--wa-color-primary);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 0 0 4px var(--wa-color-primary-subtle);
|
||||||
|
}
|
||||||
|
.stepper-item.completed .stepper-number {
|
||||||
|
background: var(--wa-color-success);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.stepper-label {
|
||||||
|
font-size: var(--wa-font-size-xs);
|
||||||
|
color: var(--wa-color-neutral-500);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
.stepper-label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stepper-item.active .stepper-label {
|
||||||
|
color: var(--wa-color-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.stepper-line {
|
||||||
|
width: 40px;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--wa-color-neutral-200);
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
.stepper-line.completed {
|
||||||
|
background: var(--wa-color-success);
|
||||||
|
}
|
||||||
|
/* Progress dots */
|
||||||
|
.progress-dots {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--wa-space-xs);
|
||||||
|
}
|
||||||
|
.progress-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--wa-color-neutral-200);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.progress-dot.active {
|
||||||
|
background: var(--wa-color-primary);
|
||||||
|
transform: scale(1.25);
|
||||||
|
}
|
||||||
|
.progress-dot.completed {
|
||||||
|
background: var(--wa-color-success);
|
||||||
|
}
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-height: 600px) {
|
||||||
|
.onboarding-stepper {
|
||||||
|
padding: var(--wa-space-s) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
304
components/stepper_templ.go
Normal file
304
components/stepper_templ.go
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.977
|
||||||
|
package components
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// StepInfo represents a single step in the stepper
|
||||||
|
type StepInfo struct {
|
||||||
|
Number int
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnboardingStepper renders a 3-step progress indicator
|
||||||
|
// currentStep: 1-indexed current step number
|
||||||
|
// steps: slice of step labels (e.g., []string{"Welcome", "Learn", "Get Started"})
|
||||||
|
func OnboardingStepper(currentStep int, steps []string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = stepperStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"onboarding-stepper\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for i, label := range steps {
|
||||||
|
templ_7745c5c3_Err = stepperItem(i+1, label, currentStep).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if i < len(steps)-1 {
|
||||||
|
templ_7745c5c3_Err = stepperLine(i+1 < currentStep).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// stepperItem renders a single step circle with label
|
||||||
|
func stepperItem(stepNum int, label string, currentStep int) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var2 == nil {
|
||||||
|
templ_7745c5c3_Var2 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
var templ_7745c5c3_Var3 = []any{"stepper-item", templ.KV("active", stepNum == currentStep), templ.KV("completed", stepNum < currentStep)}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/stepper.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\" data-step=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stepNum))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/stepper.templ`, Line: 28, Col: 154}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"><div class=\"stepper-number\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if stepNum < currentStep {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<wa-icon name=\"check\" style=\"font-size: 14px;\"></wa-icon>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stepNum))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/stepper.templ`, Line: 33, Col: 27}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div><span class=\"stepper-label\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(label)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/stepper.templ`, Line: 36, Col: 37}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</span></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// stepperLine renders the connecting line between steps
|
||||||
|
func stepperLine(completed bool) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var8 == nil {
|
||||||
|
templ_7745c5c3_Var8 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
var templ_7745c5c3_Var9 = []any{"stepper-line", templ.KV("completed", completed)}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var9).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/stepper.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressDots renders a compact dot-based progress indicator
|
||||||
|
func ProgressDots(current int, total int) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var11 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var11 == nil {
|
||||||
|
templ_7745c5c3_Var11 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"progress-dots\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for i := 1; i <= total; i++ {
|
||||||
|
var templ_7745c5c3_Var12 = []any{"progress-dot", templ.KV("active", i == current), templ.KV("completed", i < current)}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var12).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/stepper.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func stepperStyles() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var14 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var14 == nil {
|
||||||
|
templ_7745c5c3_Var14 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<style>\n\t\t.onboarding-stepper {\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: center;\n\t\t\talign-items: center;\n\t\t\tgap: var(--wa-space-s);\n\t\t\tpadding: var(--wa-space-m) 0;\n\t\t}\n\t\t.stepper-item {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--wa-space-xs);\n\t\t}\n\t\t.stepper-number {\n\t\t\twidth: 32px;\n\t\t\theight: 32px;\n\t\t\tborder-radius: 50%;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tfont-size: var(--wa-font-size-s);\n\t\t\tfont-weight: 600;\n\t\t\tbackground: var(--wa-color-neutral-200);\n\t\t\tcolor: var(--wa-color-neutral-600);\n\t\t\ttransition: all 0.3s;\n\t\t}\n\t\t.stepper-item.active .stepper-number {\n\t\t\tbackground: var(--wa-color-primary);\n\t\t\tcolor: white;\n\t\t\tbox-shadow: 0 0 0 4px var(--wa-color-primary-subtle);\n\t\t}\n\t\t.stepper-item.completed .stepper-number {\n\t\t\tbackground: var(--wa-color-success);\n\t\t\tcolor: white;\n\t\t}\n\t\t.stepper-label {\n\t\t\tfont-size: var(--wa-font-size-xs);\n\t\t\tcolor: var(--wa-color-neutral-500);\n\t\t\tdisplay: none;\n\t\t}\n\t\t@media (min-width: 480px) {\n\t\t\t.stepper-label {\n\t\t\t\tdisplay: block;\n\t\t\t}\n\t\t}\n\t\t.stepper-item.active .stepper-label {\n\t\t\tcolor: var(--wa-color-primary);\n\t\t\tfont-weight: 500;\n\t\t}\n\t\t.stepper-line {\n\t\t\twidth: 40px;\n\t\t\theight: 2px;\n\t\t\tbackground: var(--wa-color-neutral-200);\n\t\t\ttransition: background 0.3s;\n\t\t}\n\t\t.stepper-line.completed {\n\t\t\tbackground: var(--wa-color-success);\n\t\t}\n\t\t/* Progress dots */\n\t\t.progress-dots {\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--wa-space-xs);\n\t\t}\n\t\t.progress-dot {\n\t\t\twidth: 8px;\n\t\t\theight: 8px;\n\t\t\tborder-radius: 50%;\n\t\t\tbackground: var(--wa-color-neutral-200);\n\t\t\ttransition: all 0.3s;\n\t\t}\n\t\t.progress-dot.active {\n\t\t\tbackground: var(--wa-color-primary);\n\t\t\ttransform: scale(1.25);\n\t\t}\n\t\t.progress-dot.completed {\n\t\t\tbackground: var(--wa-color-success);\n\t\t}\n\t\t/* Responsive adjustments */\n\t\t@media (max-height: 600px) {\n\t\t\t.onboarding-stepper {\n\t\t\t\tpadding: var(--wa-space-s) 0;\n\t\t\t}\n\t\t}\n\t</style>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module nebula
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
|
|
||||||
|
require github.com/a-h/templ v0.3.977 // indirect
|
||||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/a-h/templ v0.3.977 h1:kiKAPXTZE2Iaf8JbtM21r54A8bCNsncrfnokZZSrSDg=
|
||||||
|
github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||||
40
handlers/routes.go
Normal file
40
handlers/routes.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"nebula/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterRoutes sets up all HTTP routes for the application
|
||||||
|
func RegisterRoutes(mux *http.ServeMux) {
|
||||||
|
// Welcome page routes
|
||||||
|
mux.HandleFunc("GET /", handleWelcome)
|
||||||
|
mux.HandleFunc("GET /welcome", handleWelcome)
|
||||||
|
mux.HandleFunc("GET /welcome/step/{step}", handleWelcomeStep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleWelcome renders the full welcome page at step 1
|
||||||
|
func handleWelcome(w http.ResponseWriter, r *http.Request) {
|
||||||
|
views.WelcomePage(1).Render(r.Context(), w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleWelcomeStep handles HTMX partial updates for step navigation
|
||||||
|
func handleWelcomeStep(w http.ResponseWriter, r *http.Request) {
|
||||||
|
stepStr := r.PathValue("step")
|
||||||
|
step, err := strconv.Atoi(stepStr)
|
||||||
|
if err != nil || step < 1 || step > 3 {
|
||||||
|
step = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is an HTMX request
|
||||||
|
if r.Header.Get("HX-Request") == "true" {
|
||||||
|
// Return only the step content fragment
|
||||||
|
views.WelcomeStepContent(step).Render(r.Context(), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full page render for non-HTMX requests
|
||||||
|
views.WelcomePage(step).Render(r.Context(), w)
|
||||||
|
}
|
||||||
1
htmx_repo
Submodule
1
htmx_repo
Submodule
Submodule htmx_repo added at 749d5f2f4c
28
layouts/base.templ
Normal file
28
layouts/base.templ
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package layouts
|
||||||
|
|
||||||
|
// Base provides the HTML document structure for all pages
|
||||||
|
templ Base(title string) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="wa-cloak">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<title>{ title } - Sonr Motr Wallet</title>
|
||||||
|
<script src="https://cdn.sonr.org/wa/autoloader.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--wa-color-primary: #17c2ff;
|
||||||
|
}
|
||||||
|
html, body {
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{ children... }
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
62
layouts/base_templ.go
Normal file
62
layouts/base_templ.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.977
|
||||||
|
package layouts
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// Base provides the HTML document structure for all pages
|
||||||
|
func Base(title string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\" class=\"wa-cloak\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `layouts/base.templ`, Line: 10, Col: 17}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, " - Sonr Motr Wallet</title><script src=\"https://cdn.sonr.org/wa/autoloader.js\"></script><script src=\"https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js\"></script><style>\n\t\t\t\t:root {\n\t\t\t\t\t--wa-color-primary: #17c2ff;\n\t\t\t\t}\n\t\t\t\thtml, body {\n\t\t\t\t\tmin-height: 100%;\n\t\t\t\t\tpadding: 0;\n\t\t\t\t\tmargin: 0;\n\t\t\t\t}\n\t\t\t</style></head><body>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</body></html>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
43
layouts/centered.templ
Normal file
43
layouts/centered.templ
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package layouts
|
||||||
|
|
||||||
|
// CenteredCard layout for auth pages (login, register, welcome, authorize)
|
||||||
|
// Optimized for popup/webview viewports: 360×540 to 480×680
|
||||||
|
templ CenteredCard(title string) {
|
||||||
|
@Base(title) {
|
||||||
|
@centeredStyles()
|
||||||
|
<wa-page>
|
||||||
|
<main class="main-centered">
|
||||||
|
<wa-card>
|
||||||
|
{ children... }
|
||||||
|
</wa-card>
|
||||||
|
</main>
|
||||||
|
</wa-page>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// centeredStyles contains the CSS for the centered card layout
|
||||||
|
templ centeredStyles() {
|
||||||
|
<style>
|
||||||
|
.main-centered {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100%;
|
||||||
|
padding: var(--wa-space-l);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.main-centered wa-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 48ch;
|
||||||
|
}
|
||||||
|
/* Viewport constraints for popup/webview (360-480px width) */
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.main-centered {
|
||||||
|
padding: var(--wa-space-m);
|
||||||
|
}
|
||||||
|
.main-centered wa-card {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
102
layouts/centered_templ.go
Normal file
102
layouts/centered_templ.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.977
|
||||||
|
package layouts
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// CenteredCard layout for auth pages (login, register, welcome, authorize)
|
||||||
|
// Optimized for popup/webview viewports: 360×540 to 480×680
|
||||||
|
func CenteredCard(title string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = centeredStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, " <wa-page><main class=\"main-centered\"><wa-card>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</wa-card></main></wa-page>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = Base(title).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// centeredStyles contains the CSS for the centered card layout
|
||||||
|
func centeredStyles() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var3 == nil {
|
||||||
|
templ_7745c5c3_Var3 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<style>\n\t\t.main-centered {\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: center;\n\t\t\talign-items: center;\n\t\t\tmin-height: 100%;\n\t\t\tpadding: var(--wa-space-l);\n\t\t\tbox-sizing: border-box;\n\t\t}\n\t\t.main-centered wa-card {\n\t\t\twidth: 100%;\n\t\t\tmax-width: 48ch;\n\t\t}\n\t\t/* Viewport constraints for popup/webview (360-480px width) */\n\t\t@media (max-width: 400px) {\n\t\t\t.main-centered {\n\t\t\t\tpadding: var(--wa-space-m);\n\t\t\t}\n\t\t\t.main-centered wa-card {\n\t\t\t\tmax-width: 100%;\n\t\t\t}\n\t\t}\n\t</style>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
18
main.go
Normal file
18
main.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"nebula/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
handlers.RegisterRoutes(mux)
|
||||||
|
|
||||||
|
addr := ":8080"
|
||||||
|
fmt.Printf("Starting server at http://localhost%s\n", addr)
|
||||||
|
log.Fatal(http.ListenAndServe(addr, mux))
|
||||||
|
}
|
||||||
64
migration-guide.md
Normal file
64
migration-guide.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
+++
|
||||||
|
title = "htmx 1.x → htmx 2.x Migration Guide"
|
||||||
|
+++
|
||||||
|
|
||||||
|
The purpose of this guide is to provide instructions for migrations from htmx 1.x to htmx 2.x.
|
||||||
|
We place a very high value on backwards compatibility, so in most cases this migrations should require very little, if any, work.
|
||||||
|
|
||||||
|
* If you are using htmx in a module setting, we now provide module-type specific files for all three of the major
|
||||||
|
JavaScript module types
|
||||||
|
* ESM Modules: `/dist/htmx.esm.js`
|
||||||
|
* AMD Modules: `/dist/htmx.amd.js`
|
||||||
|
* CJS Modules: `/dist/htmx.cjs.js`
|
||||||
|
* The `/dist/htmx.js` file continues to be browser-loadable
|
||||||
|
* All extensions have been removed from the core htmx distribution and are distributed separately. While many 1.x
|
||||||
|
extensions will continue to work with htmx 2, you
|
||||||
|
must upgrade the SSE extension to the 2.x version, and it is recommended that you upgrade all of them to the 2.x
|
||||||
|
versions.
|
||||||
|
* If you are still using the legacy `hx-ws` and `hx-sse` attributes, please upgrade to the extension versions
|
||||||
|
* Default Changes
|
||||||
|
* If you want to retain the 1.0 behavior of "smooth scrolling" by default, revert `htmx.config.scrollBehavior` to `'smooth'`
|
||||||
|
* If you want `DELETE` requests to use a form-encoded body rather than parameters, revert
|
||||||
|
`htmx.config.methodsThatUseUrlParams` to `["get"]` (it's a little crazy, but `DELETE`, according to the spec, should
|
||||||
|
use request parameters like `GET`.)
|
||||||
|
* If you want to make cross-domain requests with htmx, revert `htmx.config.selfRequestsOnly` to `false`
|
||||||
|
* Convert any `hx-on` attributes to their `hx-on:` equivalent:
|
||||||
|
```html
|
||||||
|
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')
|
||||||
|
htmx:afterRequest: alert('Done making a request!')">
|
||||||
|
Get Info!
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
becomes:
|
||||||
|
```html
|
||||||
|
<button hx-get="/info" hx-on:htmx:before-request="alert('Making a request!')"
|
||||||
|
hx-on:htmx:after-request="alert('Done making a request!')">
|
||||||
|
Get Info!
|
||||||
|
</button>
|
||||||
|
Note that you must use the kebab-case of the event name due to the fact that attributes are case-insensitive in HTML.
|
||||||
|
```
|
||||||
|
* The `htmx.makeFragment()` method now **always** returns a `DocumentFragment` rather than either an `Element` or `DocumentFragment`
|
||||||
|
* If you are an extension author and your extension was using `selectAndSwap` method from internal API, it was removed and replaced with `swap` method,
|
||||||
|
which is available from both internal and public htmx APIs
|
||||||
|
|
||||||
|
To do a swap using new method, you need to simply use
|
||||||
|
|
||||||
|
```js
|
||||||
|
let content = "<div>Hello world</div>"; // this is HTML that will be swapped into target
|
||||||
|
let target = api.getTarget(child);
|
||||||
|
let swapSpec = api.getSwapSpecification(child);
|
||||||
|
api.swap(target, content, swapSpec);
|
||||||
|
```
|
||||||
|
|
||||||
|
`swap` method documentation is available on [JS API Reference](/api/#swap)
|
||||||
|
|
||||||
|
IE is no longer supported in htmx 2.0, but [htmx 1.x](https://v1.htmx.org) continues to support IE and will be supported
|
||||||
|
for the foreseeable future.
|
||||||
|
|
||||||
|
## Upgrade Music
|
||||||
|
|
||||||
|
This is the official htmx 1.x -> 2.x upgrade music:
|
||||||
|
|
||||||
|
<iframe width="640" height="360" src="https://www.youtube.com/embed/YDkD-N5goMg" title="PYLOT - Upgrades (Visualizer)"
|
||||||
|
frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||||
|
|
||||||
65
v2-release-post.md
Normal file
65
v2-release-post.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
+++
|
||||||
|
title = "htmx 2.0.0 has been released!"
|
||||||
|
date = 2024-06-17
|
||||||
|
[taxonomies]
|
||||||
|
tag = ["posts", "announcements"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
## htmx 2.0.0 Release
|
||||||
|
|
||||||
|
I'm very happy to announce the release of htmx 2.0. This release ends support for Internet Explorer and tightens up some
|
||||||
|
defaults, but does not change most of the core functionality or the core API of the library.
|
||||||
|
|
||||||
|
Note that we are not marking 2.0 as [`latest`](https://docs.npmjs.com/cli/v10/commands/npm-dist-tag#purpose) in NPM
|
||||||
|
because we do not want to force-upgrade users who are relying on non-versioned CDN URLs for htmx. Instead, 1.x will
|
||||||
|
remain `latest` and the 2.0 line will remain `next` until Jan 1, 2025. The website, however, will reference 2.0.
|
||||||
|
|
||||||
|
### Major Changes
|
||||||
|
|
||||||
|
* All extensions have been moved out of the core repository to [their own repo](https://github.com/bigskysoftware/htmx-extensions/)
|
||||||
|
and website: <https://extensions.htmx.org>. They are now all versioned individually and can be developed outside of
|
||||||
|
the normal (slow) htmx release cadence.
|
||||||
|
* Most 1.x extensions will work with 2.x, however the SSE extension did have a break and must be upgraded.
|
||||||
|
* The older extensions remain in the `/dist/ext` directory so as to not break the URLs of CDNs like unpkg, but please
|
||||||
|
move to the new extension URLs going forward
|
||||||
|
* We removed the deprecated `hx-sse` and `hx-ws` attributes in favor of the extensions, which were available and
|
||||||
|
recommended in 1.x.
|
||||||
|
* HTTP `DELETE` requests now use parameters, rather than form encoded bodies, for their payload (This is in accordance w/ the spec.)
|
||||||
|
* We now provide specific files in `/dist` for the various JavaScript module styles:
|
||||||
|
* ESM Modules: `/dist/htmx.esm.js`
|
||||||
|
* AMD Modules: `/dist/htmx.amd.js`
|
||||||
|
* CJS Modules: `/dist/htmx.cjs.js`
|
||||||
|
* The `/dist/htmx.js` file continues to be browser-loadable
|
||||||
|
* The `hx-on` attribute, with its special syntax, has been removed in favor of the less-hacky `hx-on:` syntax.
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
* We made some default changes:
|
||||||
|
* `htmx.config.scrollBehavior` was changed to `'instant'` from `'smooth'`
|
||||||
|
* As mentioned previously, `DELETE` requests now use query parameters, rather than a form-encoded body. This can
|
||||||
|
be reverted by setting `htmx.methodsThatUseUrlParams` to the value `['get']`,
|
||||||
|
* `htmx.config.selfRequestsOnly` now defaults to `true` rather than `false`
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
Not much, really:
|
||||||
|
|
||||||
|
* The `selectAndSwap()` internal API method was replaced with the public (and much better) [`swap()`](/api/#swap) method
|
||||||
|
* Web Component support has been [improved dramatically](@/examples/web-components.md)
|
||||||
|
* And the biggest feature of this release: [the website](https://htmx.org) now supports dark mode! (Thanks [@pokonski](https://github.com/pokonski)!)
|
||||||
|
|
||||||
|
A complete upgrade guide can be found here:
|
||||||
|
|
||||||
|
[htmx 1.x -> 2.x Migration Guide](@/migration-guide-htmx-1.md)
|
||||||
|
|
||||||
|
If you require IE compatibility, the [1.x](https://v1.htmx.org) will continue to be supported for the foreseeable future.
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
|
||||||
|
htmx 2.0 can be installed via a package manager referencing version `2.0.0`, or can be linked via a CDN:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://unpkg.com/htmx.org@2.0.0/dist/htmx.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
or <a href="https://unpkg.com/htmx.org@2.0.0/dist/htmx.min.js" download>Downloaded</a>
|
||||||
343
views/welcome.templ
Normal file
343
views/welcome.templ
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"nebula/components"
|
||||||
|
"nebula/layouts"
|
||||||
|
)
|
||||||
|
|
||||||
|
var welcomeSteps = []string{"Welcome", "Learn", "Get Started"}
|
||||||
|
|
||||||
|
// WelcomePage renders the full onboarding page with the specified step
|
||||||
|
templ WelcomePage(currentStep int) {
|
||||||
|
@layouts.CenteredCard("Welcome") {
|
||||||
|
<div slot="header">
|
||||||
|
@components.OnboardingStepper(currentStep, welcomeSteps)
|
||||||
|
</div>
|
||||||
|
<div id="step-content">
|
||||||
|
@WelcomeStepContent(currentStep)
|
||||||
|
</div>
|
||||||
|
<footer slot="footer">
|
||||||
|
@WelcomeFooter()
|
||||||
|
</footer>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStepContent renders the content for a specific step (used for HTMX partials)
|
||||||
|
templ WelcomeStepContent(step int) {
|
||||||
|
switch step {
|
||||||
|
case 1:
|
||||||
|
@WelcomeStep1()
|
||||||
|
case 2:
|
||||||
|
@WelcomeStep2()
|
||||||
|
case 3:
|
||||||
|
@WelcomeStep3()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStep1 - Initial welcome screen with features
|
||||||
|
templ WelcomeStep1() {
|
||||||
|
@welcomeStyles()
|
||||||
|
<div class="wa-stack wa-gap-l">
|
||||||
|
<div class="hero-icon">
|
||||||
|
<wa-icon name="wallet" family="duotone"></wa-icon>
|
||||||
|
</div>
|
||||||
|
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
||||||
|
<h1 class="wa-heading-xl">Welcome to Sonr</h1>
|
||||||
|
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
||||||
|
Your self-sovereign identity wallet powered by WebAssembly
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<wa-divider></wa-divider>
|
||||||
|
<div class="wa-stack wa-gap-s">
|
||||||
|
@FeatureItem("shield-check", "security", "Passwordless Security", "Authenticate with biometrics or hardware keys - no passwords to remember or steal")
|
||||||
|
@FeatureItem("bolt", "speed", "WASM-Powered", "Wallet runs entirely in your browser - fast, secure, and always available")
|
||||||
|
@FeatureItem("user-shield", "privacy", "You Own Your Data", "Self-sovereign identity means your keys never leave your device")
|
||||||
|
</div>
|
||||||
|
<wa-button
|
||||||
|
variant="brand"
|
||||||
|
size="large"
|
||||||
|
style="width: 100%;"
|
||||||
|
hx-get="/welcome/step/2"
|
||||||
|
hx-target="#step-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
Learn More
|
||||||
|
<wa-icon slot="end" name="arrow-right"></wa-icon>
|
||||||
|
</wa-button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStep2 - How Motr Works explanation
|
||||||
|
templ WelcomeStep2() {
|
||||||
|
@welcomeStyles()
|
||||||
|
<div class="wa-stack wa-gap-l">
|
||||||
|
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
||||||
|
<h2 class="wa-heading-l">How Motr Works</h2>
|
||||||
|
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
||||||
|
A next-generation wallet built on WebAuthn and WASM
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<wa-divider></wa-divider>
|
||||||
|
<div class="wa-stack wa-gap-m">
|
||||||
|
@InfoCallout("microchip", "WebAssembly Runtime", "Your wallet logic runs as compiled Go code in a secure WASM sandbox, directly in your browser.")
|
||||||
|
@InfoCallout("fingerprint", "Passkey Authentication", "Use Face ID, Touch ID, or hardware security keys. Your biometrics stay on your device.")
|
||||||
|
@InfoCallout("network-wired", "Decentralized Identity", "Connect to any app with OpenID Connect - you control what data to share.")
|
||||||
|
@InfoCallout("key", "Multi-Chain Support", "One wallet for Sonr, Ethereum, Cosmos, Bitcoin and more via IBC.")
|
||||||
|
</div>
|
||||||
|
<div class="wa-cluster wa-gap-s" style="justify-content: space-between;">
|
||||||
|
<wa-button
|
||||||
|
variant="neutral"
|
||||||
|
appearance="outlined"
|
||||||
|
hx-get="/welcome/step/1"
|
||||||
|
hx-target="#step-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<wa-icon slot="start" name="arrow-left"></wa-icon>
|
||||||
|
Back
|
||||||
|
</wa-button>
|
||||||
|
<wa-button
|
||||||
|
variant="brand"
|
||||||
|
hx-get="/welcome/step/3"
|
||||||
|
hx-target="#step-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
Get Started
|
||||||
|
<wa-icon slot="end" name="arrow-right"></wa-icon>
|
||||||
|
</wa-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStep3 - Action selection (Create/Sign In)
|
||||||
|
templ WelcomeStep3() {
|
||||||
|
@welcomeStyles()
|
||||||
|
<div class="wa-stack wa-gap-l">
|
||||||
|
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
||||||
|
<h2 class="wa-heading-l">Ready to Begin?</h2>
|
||||||
|
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
||||||
|
Create a new wallet or sign in to an existing one
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<wa-divider></wa-divider>
|
||||||
|
<div class="wa-grid" style="--min-column-size: 180px; gap: var(--wa-space-m);">
|
||||||
|
<a href="/register" class="action-card">
|
||||||
|
<wa-icon name="user-plus" family="duotone"></wa-icon>
|
||||||
|
<div class="wa-stack wa-gap-2xs">
|
||||||
|
<span class="wa-heading-m">Create Wallet</span>
|
||||||
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||||||
|
New to Sonr? Set up your wallet in minutes
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="/login" class="action-card">
|
||||||
|
<wa-icon name="right-to-bracket" family="duotone"></wa-icon>
|
||||||
|
<div class="wa-stack wa-gap-2xs">
|
||||||
|
<span class="wa-heading-m">Sign In</span>
|
||||||
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||||||
|
Access your existing wallet
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<wa-divider>or</wa-divider>
|
||||||
|
<wa-button
|
||||||
|
variant="neutral"
|
||||||
|
appearance="outlined"
|
||||||
|
style="width: 100%;"
|
||||||
|
hx-get="/welcome/qr-scanner"
|
||||||
|
hx-target="#step-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<wa-icon slot="start" name="qrcode"></wa-icon>
|
||||||
|
Scan QR Code
|
||||||
|
</wa-button>
|
||||||
|
<wa-callout variant="brand" appearance="filled">
|
||||||
|
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||||||
|
<span class="wa-caption-s">
|
||||||
|
Already have a passkey registered on another device? Use QR code to sync your wallet.
|
||||||
|
</span>
|
||||||
|
</wa-callout>
|
||||||
|
<wa-button
|
||||||
|
variant="neutral"
|
||||||
|
appearance="plain"
|
||||||
|
style="width: 100%;"
|
||||||
|
hx-get="/welcome/step/2"
|
||||||
|
hx-target="#step-content"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<wa-icon slot="start" name="arrow-left"></wa-icon>
|
||||||
|
Back to How It Works
|
||||||
|
</wa-button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeFooter renders the network status and links footer
|
||||||
|
templ WelcomeFooter() {
|
||||||
|
@welcomeStyles()
|
||||||
|
<div class="wa-stack wa-gap-m wa-align-items-center">
|
||||||
|
<div class="network-status">
|
||||||
|
<div class="status-dot"></div>
|
||||||
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||||||
|
Sonr Network: <strong style="color: var(--wa-color-success);">Operational</strong>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="wa-cluster wa-justify-content-center wa-gap-m">
|
||||||
|
<wa-button appearance="plain" size="small" onclick="window.open('https://sonr.io', '_blank')">
|
||||||
|
Learn about Sonr
|
||||||
|
</wa-button>
|
||||||
|
<span style="color: var(--wa-color-neutral-300);">|</span>
|
||||||
|
<wa-button appearance="plain" size="small" onclick="window.open('https://docs.sonr.io', '_blank')">
|
||||||
|
Documentation
|
||||||
|
</wa-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeatureItem renders a feature highlight with icon
|
||||||
|
templ FeatureItem(icon string, variant string, title string, description string) {
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class={ "feature-icon", variant }>
|
||||||
|
<wa-icon name={ icon }></wa-icon>
|
||||||
|
</div>
|
||||||
|
<div class="wa-stack wa-gap-2xs">
|
||||||
|
<span class="wa-heading-xs">{ title }</span>
|
||||||
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||||||
|
{ description }
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoCallout renders an informational callout box
|
||||||
|
templ InfoCallout(icon string, title string, description string) {
|
||||||
|
<wa-callout variant="neutral">
|
||||||
|
<wa-icon slot="icon" name={ icon }></wa-icon>
|
||||||
|
<div class="wa-stack wa-gap-2xs">
|
||||||
|
<span class="wa-heading-xs">{ title }</span>
|
||||||
|
<span class="wa-caption-s">{ description }</span>
|
||||||
|
</div>
|
||||||
|
</wa-callout>
|
||||||
|
}
|
||||||
|
|
||||||
|
// welcomeStyles contains the CSS specific to the welcome page
|
||||||
|
templ welcomeStyles() {
|
||||||
|
<style>
|
||||||
|
.hero-icon {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: var(--wa-radius-l);
|
||||||
|
background: linear-gradient(135deg, var(--wa-color-primary), #0090ff);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto var(--wa-space-l);
|
||||||
|
box-shadow: 0 8px 32px rgba(23, 194, 255, 0.3);
|
||||||
|
}
|
||||||
|
.hero-icon wa-icon {
|
||||||
|
font-size: 40px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.feature-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--wa-space-m);
|
||||||
|
padding: var(--wa-space-m);
|
||||||
|
background: var(--wa-color-surface-alt);
|
||||||
|
border-radius: var(--wa-radius-m);
|
||||||
|
}
|
||||||
|
.feature-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: var(--wa-radius-s);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.feature-icon.security {
|
||||||
|
background: var(--wa-color-success-subtle);
|
||||||
|
color: var(--wa-color-success);
|
||||||
|
}
|
||||||
|
.feature-icon.speed {
|
||||||
|
background: var(--wa-color-primary-subtle);
|
||||||
|
color: var(--wa-color-primary);
|
||||||
|
}
|
||||||
|
.feature-icon.privacy {
|
||||||
|
background: var(--wa-color-warning-subtle);
|
||||||
|
color: var(--wa-color-warning);
|
||||||
|
}
|
||||||
|
.action-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--wa-space-m);
|
||||||
|
padding: var(--wa-space-xl);
|
||||||
|
background: var(--wa-color-surface-alt);
|
||||||
|
border-radius: var(--wa-radius-l);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
.action-card:hover {
|
||||||
|
border-color: var(--wa-color-primary);
|
||||||
|
background: var(--wa-color-primary-subtle);
|
||||||
|
}
|
||||||
|
.action-card wa-icon {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: var(--wa-color-primary);
|
||||||
|
}
|
||||||
|
.network-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--wa-space-xs);
|
||||||
|
}
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--wa-color-success);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
||||||
|
/* Viewport constraints for popup/webview */
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.hero-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
.hero-icon wa-icon {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
.feature-item {
|
||||||
|
padding: var(--wa-space-s);
|
||||||
|
}
|
||||||
|
.feature-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.action-card {
|
||||||
|
padding: var(--wa-space-m);
|
||||||
|
}
|
||||||
|
.action-card wa-icon {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-height: 600px) {
|
||||||
|
.hero-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
margin-bottom: var(--wa-space-m);
|
||||||
|
}
|
||||||
|
.hero-icon wa-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
495
views/welcome_templ.go
Normal file
495
views/welcome_templ.go
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.977
|
||||||
|
package views
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"nebula/components"
|
||||||
|
"nebula/layouts"
|
||||||
|
)
|
||||||
|
|
||||||
|
var welcomeSteps = []string{"Welcome", "Learn", "Get Started"}
|
||||||
|
|
||||||
|
// WelcomePage renders the full onboarding page with the specified step
|
||||||
|
func WelcomePage(currentStep int) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div slot=\"header\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = components.OnboardingStepper(currentStep, welcomeSteps).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div><div id=\"step-content\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = WelcomeStepContent(currentStep).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div><footer slot=\"footer\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = WelcomeFooter().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</footer>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = layouts.CenteredCard("Welcome").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStepContent renders the content for a specific step (used for HTMX partials)
|
||||||
|
func WelcomeStepContent(step int) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var3 == nil {
|
||||||
|
templ_7745c5c3_Var3 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
switch step {
|
||||||
|
case 1:
|
||||||
|
templ_7745c5c3_Err = WelcomeStep1().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
templ_7745c5c3_Err = WelcomeStep2().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
templ_7745c5c3_Err = WelcomeStep3().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStep1 - Initial welcome screen with features
|
||||||
|
func WelcomeStep1() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var4 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var4 == nil {
|
||||||
|
templ_7745c5c3_Var4 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = welcomeStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"wa-stack wa-gap-l\"><div class=\"hero-icon\"><wa-icon name=\"wallet\" family=\"duotone\"></wa-icon></div><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h1 class=\"wa-heading-xl\">Welcome to Sonr</h1><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">Your self-sovereign identity wallet powered by WebAssembly</p></div><wa-divider></wa-divider><div class=\"wa-stack wa-gap-s\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = FeatureItem("shield-check", "security", "Passwordless Security", "Authenticate with biometrics or hardware keys - no passwords to remember or steal").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = FeatureItem("bolt", "speed", "WASM-Powered", "Wallet runs entirely in your browser - fast, secure, and always available").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = FeatureItem("user-shield", "privacy", "You Own Your Data", "Self-sovereign identity means your keys never leave your device").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><wa-button variant=\"brand\" size=\"large\" style=\"width: 100%;\" hx-get=\"/welcome/step/2\" hx-target=\"#step-content\" hx-swap=\"innerHTML\">Learn More <wa-icon slot=\"end\" name=\"arrow-right\"></wa-icon></wa-button></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStep2 - How Motr Works explanation
|
||||||
|
func WelcomeStep2() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var5 == nil {
|
||||||
|
templ_7745c5c3_Var5 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = welcomeStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"wa-stack wa-gap-l\"><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h2 class=\"wa-heading-l\">How Motr Works</h2><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">A next-generation wallet built on WebAuthn and WASM</p></div><wa-divider></wa-divider><div class=\"wa-stack wa-gap-m\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = InfoCallout("microchip", "WebAssembly Runtime", "Your wallet logic runs as compiled Go code in a secure WASM sandbox, directly in your browser.").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = InfoCallout("fingerprint", "Passkey Authentication", "Use Face ID, Touch ID, or hardware security keys. Your biometrics stay on your device.").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = InfoCallout("network-wired", "Decentralized Identity", "Connect to any app with OpenID Connect - you control what data to share.").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = InfoCallout("key", "Multi-Chain Support", "One wallet for Sonr, Ethereum, Cosmos, Bitcoin and more via IBC.").Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div><div class=\"wa-cluster wa-gap-s\" style=\"justify-content: space-between;\"><wa-button variant=\"neutral\" appearance=\"outlined\" hx-get=\"/welcome/step/1\" hx-target=\"#step-content\" hx-swap=\"innerHTML\"><wa-icon slot=\"start\" name=\"arrow-left\"></wa-icon> Back</wa-button> <wa-button variant=\"brand\" hx-get=\"/welcome/step/3\" hx-target=\"#step-content\" hx-swap=\"innerHTML\">Get Started <wa-icon slot=\"end\" name=\"arrow-right\"></wa-icon></wa-button></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeStep3 - Action selection (Create/Sign In)
|
||||||
|
func WelcomeStep3() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var6 == nil {
|
||||||
|
templ_7745c5c3_Var6 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = welcomeStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"wa-stack wa-gap-l\"><div class=\"wa-stack wa-gap-xs\" style=\"text-align: center;\"><h2 class=\"wa-heading-l\">Ready to Begin?</h2><p class=\"wa-caption-m\" style=\"color: var(--wa-color-neutral-600);\">Create a new wallet or sign in to an existing one</p></div><wa-divider></wa-divider><div class=\"wa-grid\" style=\"--min-column-size: 180px; gap: var(--wa-space-m);\"><a href=\"/register\" class=\"action-card\"><wa-icon name=\"user-plus\" family=\"duotone\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-m\">Create Wallet</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">New to Sonr? Set up your wallet in minutes</span></div></a> <a href=\"/login\" class=\"action-card\"><wa-icon name=\"right-to-bracket\" family=\"duotone\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-m\">Sign In</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">Access your existing wallet</span></div></a></div><wa-divider>or</wa-divider> <wa-button variant=\"neutral\" appearance=\"outlined\" style=\"width: 100%;\" hx-get=\"/welcome/qr-scanner\" hx-target=\"#step-content\" hx-swap=\"innerHTML\"><wa-icon slot=\"start\" name=\"qrcode\"></wa-icon> Scan QR Code</wa-button> <wa-callout variant=\"brand\" appearance=\"filled\"><wa-icon slot=\"icon\" name=\"circle-info\"></wa-icon> <span class=\"wa-caption-s\">Already have a passkey registered on another device? Use QR code to sync your wallet.</span></wa-callout> <wa-button variant=\"neutral\" appearance=\"plain\" style=\"width: 100%;\" hx-get=\"/welcome/step/2\" hx-target=\"#step-content\" hx-swap=\"innerHTML\"><wa-icon slot=\"start\" name=\"arrow-left\"></wa-icon> Back to How It Works</wa-button></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WelcomeFooter renders the network status and links footer
|
||||||
|
func WelcomeFooter() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var7 == nil {
|
||||||
|
templ_7745c5c3_Var7 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = welcomeStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"wa-stack wa-gap-m wa-align-items-center\"><div class=\"network-status\"><div class=\"status-dot\"></div><span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-600);\">Sonr Network: <strong style=\"color: var(--wa-color-success);\">Operational</strong></span></div><div class=\"wa-cluster wa-justify-content-center wa-gap-m\"><wa-button appearance=\"plain\" size=\"small\" onclick=\"window.open('https://sonr.io', '_blank')\">Learn about Sonr</wa-button> <span style=\"color: var(--wa-color-neutral-300);\">|</span> <wa-button appearance=\"plain\" size=\"small\" onclick=\"window.open('https://docs.sonr.io', '_blank')\">Documentation</wa-button></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeatureItem renders a feature highlight with icon
|
||||||
|
func FeatureItem(icon string, variant string, title string, description string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var8 == nil {
|
||||||
|
templ_7745c5c3_Var8 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div class=\"feature-item\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 = []any{"feature-icon", variant}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var10 string
|
||||||
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var9).String())
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 1, Col: 0}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"><wa-icon name=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var11 string
|
||||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(icon)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 200, Col: 23}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"></wa-icon></div><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-xs\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var12 string
|
||||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 203, Col: 38}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span> <span class=\"wa-caption-s\" style=\"color: var(--wa-color-neutral-500);\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(description)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 205, Col: 17}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</span></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoCallout renders an informational callout box
|
||||||
|
func InfoCallout(icon string, title string, description string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var14 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var14 == nil {
|
||||||
|
templ_7745c5c3_Var14 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<wa-callout variant=\"neutral\"><wa-icon slot=\"icon\" name=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var15 string
|
||||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(icon)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 214, Col: 34}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"></wa-icon><div class=\"wa-stack wa-gap-2xs\"><span class=\"wa-heading-xs\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var16 string
|
||||||
|
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 216, Col: 38}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</span> <span class=\"wa-caption-s\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var17 string
|
||||||
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(description)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/welcome.templ`, Line: 217, Col: 43}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</span></div></wa-callout>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// welcomeStyles contains the CSS specific to the welcome page
|
||||||
|
func welcomeStyles() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var18 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var18 == nil {
|
||||||
|
templ_7745c5c3_Var18 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<style>\n\t\t.hero-icon {\n\t\t\twidth: 80px;\n\t\t\theight: 80px;\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t\tbackground: linear-gradient(135deg, var(--wa-color-primary), #0090ff);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tmargin: 0 auto var(--wa-space-l);\n\t\t\tbox-shadow: 0 8px 32px rgba(23, 194, 255, 0.3);\n\t\t}\n\t\t.hero-icon wa-icon {\n\t\t\tfont-size: 40px;\n\t\t\tcolor: white;\n\t\t}\n\t\t.feature-item {\n\t\t\tdisplay: flex;\n\t\t\talign-items: flex-start;\n\t\t\tgap: var(--wa-space-m);\n\t\t\tpadding: var(--wa-space-m);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-m);\n\t\t}\n\t\t.feature-icon {\n\t\t\twidth: 40px;\n\t\t\theight: 40px;\n\t\t\tborder-radius: var(--wa-radius-s);\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tflex-shrink: 0;\n\t\t}\n\t\t.feature-icon.security {\n\t\t\tbackground: var(--wa-color-success-subtle);\n\t\t\tcolor: var(--wa-color-success);\n\t\t}\n\t\t.feature-icon.speed {\n\t\t\tbackground: var(--wa-color-primary-subtle);\n\t\t\tcolor: var(--wa-color-primary);\n\t\t}\n\t\t.feature-icon.privacy {\n\t\t\tbackground: var(--wa-color-warning-subtle);\n\t\t\tcolor: var(--wa-color-warning);\n\t\t}\n\t\t.action-card {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tgap: var(--wa-space-m);\n\t\t\tpadding: var(--wa-space-xl);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t\tborder: 2px solid transparent;\n\t\t\tcursor: pointer;\n\t\t\ttransition: all 0.2s;\n\t\t\ttext-align: center;\n\t\t\ttext-decoration: none;\n\t\t\tcolor: inherit;\n\t\t}\n\t\t.action-card:hover {\n\t\t\tborder-color: var(--wa-color-primary);\n\t\t\tbackground: var(--wa-color-primary-subtle);\n\t\t}\n\t\t.action-card wa-icon {\n\t\t\tfont-size: 2.5rem;\n\t\t\tcolor: var(--wa-color-primary);\n\t\t}\n\t\t.network-status {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: var(--wa-space-xs);\n\t\t}\n\t\t.status-dot {\n\t\t\twidth: 8px;\n\t\t\theight: 8px;\n\t\t\tborder-radius: 50%;\n\t\t\tbackground: var(--wa-color-success);\n\t\t\tanimation: pulse 2s infinite;\n\t\t}\n\t\t@keyframes pulse {\n\t\t\t0%, 100% { opacity: 1; }\n\t\t\t50% { opacity: 0.5; }\n\t\t}\n\t\t/* Viewport constraints for popup/webview */\n\t\t@media (max-width: 400px) {\n\t\t\t.hero-icon {\n\t\t\t\twidth: 64px;\n\t\t\t\theight: 64px;\n\t\t\t}\n\t\t\t.hero-icon wa-icon {\n\t\t\t\tfont-size: 32px;\n\t\t\t}\n\t\t\t.feature-item {\n\t\t\t\tpadding: var(--wa-space-s);\n\t\t\t}\n\t\t\t.feature-icon {\n\t\t\t\twidth: 32px;\n\t\t\t\theight: 32px;\n\t\t\t}\n\t\t\t.action-card {\n\t\t\t\tpadding: var(--wa-space-m);\n\t\t\t}\n\t\t\t.action-card wa-icon {\n\t\t\t\tfont-size: 2rem;\n\t\t\t}\n\t\t}\n\t\t@media (max-height: 600px) {\n\t\t\t.hero-icon {\n\t\t\t\twidth: 56px;\n\t\t\t\theight: 56px;\n\t\t\t\tmargin-bottom: var(--wa-space-m);\n\t\t\t}\n\t\t\t.hero-icon wa-icon {\n\t\t\t\tfont-size: 28px;\n\t\t\t}\n\t\t}\n\t</style>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
Reference in New Issue
Block a user