Compare commits

...

283 Commits

Author SHA1 Message Date
Greg Johnston
52c5a88e53 Construct all fragments lazily (closes #299 and #421) 2023-01-31 23:34:33 -05:00
Greg Johnston
25dfd88978 Add html-escape dependency 2023-01-31 23:26:31 -05:00
Greg Johnston
066ad56dbd Fix HTML entities 2023-01-31 22:31:15 -05:00
Greg Johnston
decd5fbfdb clippy 2023-01-31 22:28:01 -05:00
Greg Johnston
fdb6425504 Correctly escape special HTML characters in static strings in view macro 2023-01-31 22:07:17 -05:00
Ben Wishovich
160f336303 Update ErrorBoundary to use miette::Diagnostic instead of Error, and various other tweaks (#401)
* Switch RwLock to parking_lot so they are no longer async
* cleanup todo_app_sqlite_axum
* add errors_axum example

---------

Co-authored-by: Indrazar <110272232+Indrazar@users.noreply.github.com>
2023-01-31 21:56:42 -05:00
starmaker
e2b1365e46 Implemented update_returning for StoredValue (#419) 2023-01-31 17:40:39 -05:00
Greg Johnston
45eee12b18 Fix issues with attribute names in SSR (#418) 2023-01-31 11:57:05 -05:00
Bruno De Simone
e2cdbc746f Add leptos_routes functions for integrations (#415)
* added leptos_routes_with_context

* added leptos_routes_with_handler for axum integration
2023-01-31 09:09:58 -05:00
Ben Wishovich
48cf8d9382 Switch RwLock to parking_lot so they are no longer async (#414) 2023-01-30 20:11:56 -05:00
Greg Johnston
42e50327a6 Fix <option> and <use> top-level types in SSR (#416) 2023-01-30 20:10:07 -05:00
martin frances
ea0e2ce363 Escape <HTML> and <BODY> tokens in documentation markup. (#410) 2023-01-30 19:17:41 -05:00
martin frances
465cbc36be Minor: Bump typed-builder from 0.11 to 0.12. (#409) 2023-01-30 19:17:09 -05:00
Greg Johnston
62061f90ea Add <Html/> and <Body/> components in leptos_meta (#407)
Closes #376.
2023-01-29 19:07:48 -05:00
Greg Johnston
9a231ddef0 Merge pull request #408 from leptos-rs/fix-boolean-attributes-ssr
Fix boolean attributes in `view` macro fast-path SSR
2023-01-29 18:43:21 -05:00
Greg Johnston
ce6a093f9f oops 2023-01-29 17:11:02 -05:00
Greg Johnston
f07fa0e0be escape attributes 2023-01-29 16:55:28 -05:00
Greg Johnston
43ad91512a Fixes boolean attributes in SSR (closes #405) 2023-01-29 16:29:06 -05:00
Greg Johnston
116d23f2c3 Revert "fix: Fixes boolean attributes in HTML fast-path (closes issue #405)"
This reverts commit 2ecb345a79.
2023-01-29 16:27:28 -05:00
Greg Johnston
2ecb345a79 fix: Fixes boolean attributes in HTML fast-path (closes issue #405) 2023-01-29 16:02:47 -05:00
Greg Johnston
f55f833426 Merge pull request #403 from leptos-rs/children-type-alias
Add `Children` type alias
2023-01-29 07:07:09 -05:00
Greg Johnston
7101a2f55e Add Children type alias 2023-01-28 22:32:00 -05:00
Greg Johnston
f8b76387ec Fix labels in parent_child README 2023-01-28 21:52:16 -05:00
Greg Johnston
11fc51577b 0.1.3 2023-01-28 12:12:09 -05:00
Greg Johnston
ae1ca969ef Merge pull request #397 from leptos-rs/v0.1.2
`v0.1.2`
2023-01-28 11:17:30 -05:00
Greg Johnston
895f9d8487 Missing web-sys types 2023-01-28 08:19:13 -05:00
Greg Johnston
1e45b182a0 Fix <ErrorBoundary/> removal behavior 2023-01-28 08:14:32 -05:00
Greg Johnston
4c26dc597d Docs for <Show/> component 2023-01-28 08:07:23 -05:00
Greg Johnston
2863d49a1c Docs for <ErrorBoundary/> 2023-01-28 07:54:13 -05:00
Greg Johnston
087eb18c8b Merge pull request #396 from leptos-rs/hydration-fix-small-wasm
Fix hydration issue related to WASM size reduction
2023-01-28 07:16:23 -05:00
Greg Johnston
c7c672717c Fix hydration issue related to WASM size reduction 2023-01-28 07:16:08 -05:00
Greg Johnston
c69cc02f30 Merge pull request #393 from leptos-rs/small-wasm
Reduce WASM binary sizes by 3-5%
2023-01-28 07:08:52 -05:00
Greg Johnston
9eb81f00f9 Merge pull request #395 from thomasqueirozb/main
Fix gtk example
2023-01-28 07:08:43 -05:00
Thomas Queiroz
72fe3d45f0 Fix gtk example 2023-01-28 01:14:02 -03:00
Greg Johnston
7802d941bd cargo fmt 2023-01-27 17:04:34 -05:00
Greg Johnston
f10784f686 Merge pull request #391 from leptos-rs/remove-gloo-dependency
Remove `gloo` dependency in `leptos_dom`
2023-01-27 16:58:54 -05:00
Greg Johnston
35197691c0 Merge pull request #392 from martinfrances107/cargo_outdated
doc/book updated leptos version.
2023-01-27 16:58:41 -05:00
Greg Johnston
4afbef87f6 clippy stuff 2023-01-27 16:56:22 -05:00
Greg Johnston
218485e3be Make helpers into concrete functions for WASM binary size purposes 2023-01-27 16:24:05 -05:00
Greg Johnston
8d60a191eb Missing Storage dependency (now that gloo is gone) 2023-01-27 15:36:20 -05:00
Greg Johnston
1ba01a46af Use a concrete helper function to generate elements 2023-01-27 15:33:28 -05:00
Martin
fdece25051 BugFix, ch03 properly construct the "input_element". 2023-01-27 20:04:29 +00:00
Greg Johnston
590056e047 Remove gloo dependency in leptos_dom 2023-01-27 14:01:07 -05:00
Martin
817bb1628e doc/book updated leptos version. 2023-01-27 19:00:31 +00:00
Greg Johnston
f911cdd56f Merge pull request #390 from leptos-rs/action-form-clear-input
fix: Align `<ActionForm/>` behavior with `Action`
2023-01-27 13:53:31 -05:00
Greg Johnston
76a9c719a3 Fix missing docs error (#389) 2023-01-27 12:29:22 -05:00
Greg Johnston
395336a8c0 Correctly set pending state with ActionForm 2023-01-27 12:19:20 -05:00
Greg Johnston
b84906e6dc ActionForm should clear input as Action::dispatch() does 2023-01-27 12:15:50 -05:00
Greg Johnston
1563d237d0 Check uniqueness of server function names at registration time (#388)
* Check uniqueness of server function names at registration time, and stop leaking src file path in release mode

* Fix missing dev-dependency
2023-01-27 06:57:32 -05:00
Greg Johnston
b861f84e40 Fix a large number of small issues in docs (#386)
* Fix example links in docs

* Restore missing CSR READMEs

* Document need to enable features on `leptos_router` and `leptos_meta`

* Add "Is it production ready?" to FAQs

* Document which types are provided as contexts in server integrations

* Fix broken links and other issues in docs
2023-01-26 21:44:01 -05:00
Greg Johnston
62812af5b2 Allow unused cx in server fn arguments (#385)
* Suppress warning for unused `cx` in server function arguments
2023-01-26 21:43:39 -05:00
Greg Johnston
f300e7fd41 implements From<Signal<T>> for MaybeSignal<T> (#384) 2023-01-26 21:43:21 -05:00
Greg Johnston
44974fcf69 Replace site-address with site-addr in cargo-leptos example Cargo.toml files 2023-01-26 19:52:47 -05:00
Gentle
815c2e6dc2 leptos_axum::handle_server_fns was also duplicated (#383) 2023-01-26 15:53:31 -05:00
Roland Fredenhagen
2fc20d8312 added hgroup element (#379) 2023-01-26 15:05:58 -05:00
Gentle
679692e202 cloning is not needed here (#381) 2023-01-26 13:05:44 -05:00
Gentle
be1343fa88 refactor to eliminate duplicate code (#380) 2023-01-26 13:04:59 -05:00
Greg Johnston
fc7199f188 Fix context in outlets (#374)
* Add `Scope::parent()` to make access to parent `Scope` possible.

* Handle context properly in nested routes
2023-01-25 22:02:43 -05:00
Markus Kohlhase
154e42f3f4 Add a counter example that does not use macros (#373) 2023-01-25 21:10:16 -05:00
Ben Wishovich
4c24795ffd Make Errors Sync (#372) 2023-01-25 20:15:47 -05:00
IcosaHedron
f2e7b00d5a Fix CSR with Trunk on hackernews example, remove CSR option from isomorphic example (#369)
* Fix CSR with Trunk on hackernews example

* Update isomorphic example to remove CSR from Readme
2023-01-25 20:15:12 -05:00
Markus Kohlhase
0b36b68846 Replace urlencoding with percent-encoding (#365)
Motivation: `percent-encoding` is from the Servo team and part of the `url` crate.
2023-01-25 20:15:00 -05:00
Ben Wishovich
f24bad4bf2 Add <Show/> component to avoid rerendering of closures and tweak ErrorBoundary (#363)
Add once_cell to leptos, and add Show component! Modify ErrorBoundary to
take a closure that implements IntoView, not View
2023-01-24 10:58:25 -05:00
Greg Johnston
a2ea1d8483 Reorganize snake-case #[component] docs and please clippy (#362) 2023-01-23 11:14:04 -05:00
Ben Wishovich
9b0fb63632 Add methods to take Actix/Axum Extractors/Route Info/Stuff and pass it to Leptos (#359) 2023-01-23 07:28:05 -05:00
Greg Johnston
2febaf6b99 Merge pull request #358 from martinfrances107/bump_base64
leptos_reactive base64 bump version to 0.21.
2023-01-23 07:27:01 -05:00
Greg Johnston
6c8e8e9ce7 Merge pull request #353 from martinfrances107/redundant_clone
Clippy fixes: redundant clone and .to_string() issues.
2023-01-23 07:26:50 -05:00
Martin
7aa0181192 Removed unused variables. 2023-01-23 09:46:28 +00:00
Martin
8496bd59ce leptos_reactive base64 bump version to 0.21. 2023-01-22 22:15:54 +00:00
Greg Johnston
fd6e63796e Merge pull request #354 from jclmnop/feat/allow-snake-case-components
Allow snake case components
2023-01-22 16:46:47 -05:00
jclmnop
39cddfc82d update docs for component macro 2023-01-22 17:13:24 +00:00
jclmnop
d1333a3402 modify component attribute macro to allow snake_case fn names 2023-01-22 14:04:36 +00:00
Martin
7f9919e2d5 Clippy fixes: redundant clone, .to_string() issues. 2023-01-22 14:03:15 +00:00
Greg Johnston
fc2d6ef19d Merge pull request #343 from killertux/fix/fix-query-params-parser 2023-01-21 17:23:39 -05:00
Greg Johnston
a5531b1a7c Merge pull request #338 from benwis/error-handling
ErrorBoundary Component
2023-01-21 16:03:48 -05:00
benwis
81ab77e8ea One more time! 2023-01-21 11:54:55 -08:00
benwis
23bd399239 I did, I did break it 2023-01-21 11:25:36 -08:00
benwis
3e04318082 Remove extra 2023-01-21 10:58:47 -08:00
benwis
2d88524354 Wrap cfg_if to prevent on_cleanup from panicing on the server 2023-01-21 10:56:38 -08:00
Greg Johnston
ecb784e422 Merge pull request #352 from leptos-rs/gbj-patch-1
Update html.rs
2023-01-21 13:07:07 -05:00
Greg Johnston
69e02bfce2 Update html.rs
Yikes! Fix broken format string.
2023-01-21 13:07:00 -05:00
Greg Johnston
a75abb9e04 Merge pull request #351 from leptos-rs/view-styling
Add support for `class = ...`, in `view` macro to support scoped styling
2023-01-21 12:56:21 -05:00
Greg Johnston
bf1ef1b7c2 Fix missing {} after cleaning up unnecessary formats 2023-01-21 11:42:52 -05:00
Greg Johnston
7fb7bb90f8 Merge pull request #350 from leptos-rs/fix-script
Add SVG `<script>`, `<style>`, and `<title>` to set of ambiguous elements
2023-01-21 09:52:53 -05:00
Greg Johnston
a22a693de7 Add support for class = ..., in view macro to support scoped styling solutions 2023-01-21 09:52:05 -05:00
Clemente
cbb1e4c9d2 Update docs 2023-01-21 11:19:28 -03:00
Clemente
dbccf525ac Added some tests 2023-01-21 11:17:25 -03:00
Greg Johnston
ed6d6ae4b0 Add node_ref to docs 2023-01-21 07:26:06 -05:00
Greg Johnston
89ee88d75e Add SVG <script>, <style>, and <title> to set of ambiguous elements — closes #349 2023-01-21 07:23:32 -05:00
benwis
9ea604f516 Merge branch 'main' into error-handling 2023-01-20 15:53:18 -08:00
benwis
b5ab7b107a Test of SSR/Hydration of ErrorBoundary 2023-01-20 15:52:43 -08:00
Clemente
18eecd9606 Use URLSearchParams to handle client side query param logic 2023-01-20 18:11:49 -03:00
Greg Johnston
a49dfd3f8e Merge pull request #344 from leptos-rs/view-ssr
Reenable optimizations for SSR using the `view!` macro
2023-01-20 15:14:06 -05:00
Greg Johnston
7075f58451 Merge pull request #347 from imalexlab/doc/add-port-leptos-watch
doc: add link for leptos watch
2023-01-20 14:29:19 -05:00
Kompreni
bcabdddce5 doc: add link for leptos watch 2023-01-20 20:14:09 +01:00
Greg Johnston
726393c446 Fix SSR tests 2023-01-20 13:45:23 -05:00
Greg Johnston
c336eb8769 0.1.1 2023-01-20 13:24:05 -05:00
Greg Johnston
0f5f0de410 Merge pull request #346 from leptos-rs/suspense-comments
Change `<Suspense/>` to a specialized type that uses comments for SSR
2023-01-20 13:18:41 -05:00
Greg Johnston
3d769c9f21 Clean up some rendering issues and the panic when cleaning up 2023-01-20 13:14:09 -05:00
Greg Johnston
9ac0f0a579 Fix todomvc 2023-01-20 12:18:31 -05:00
Greg Johnston
a385f502b6 Merge pull request #345 from leptos-rs/provide-route-contexts-when-equal
Fix behavior of `RouteContext` in nested routes with different parameters
2023-01-20 12:13:43 -05:00
Greg Johnston
603eead12d cargo fmt 2023-01-20 12:06:04 -05:00
Greg Johnston
8a73f3b879 Use comment nodes for <Suspense/> to avoid both hydration and styling issues 2023-01-20 12:05:21 -05:00
Greg Johnston
5fc8907b85 Remove extraneous log 2023-01-20 10:37:14 -05:00
Greg Johnston
678990194f Update RouteContext.path() value when params change but route has not change (closes #340) 2023-01-20 10:36:48 -05:00
Greg Johnston
a964e89d1a cargo fmt 2023-01-20 10:08:13 -05:00
Greg Johnston
285092a467 Reenable SSR benchmarks 2023-01-20 10:00:16 -05:00
Greg Johnston
c1c74ead0f Get view-macro SSR optimization working 2023-01-20 09:47:16 -05:00
Clemente
e4c9109278 Fix query params behaviour difference between SSR and Hydrate 2023-01-20 09:13:18 -03:00
benwis
64add54de6 Add example error template and give the ability to access error info
inside it
2023-01-19 14:57:34 -08:00
benwis
ac343427e7 Remember all the files 2023-01-18 23:21:08 -08:00
benwis
452e397048 Made todo_app_sqlite_axum throw an error 2023-01-18 16:47:22 -08:00
benwis
ad3ac5ad3c Remove silly thing 2023-01-18 16:03:53 -08:00
benwis
db63eda2f5 Push gbj's updates 2023-01-18 16:02:06 -08:00
benwis
6cbdc57f7a Add working impl of ErrorBoundary 2023-01-18 15:49:42 -08:00
benwis
3215e44c9a Create example of file_and_error handler for Axum, and create <ErrorBoundary/>
for leptos
2023-01-18 12:59:15 -08:00
Greg Johnston
b54a60213b Merge pull request #333 from leptos-rs/minimize-runtime-panics
Minimize panics when runtime has already been disposed (e.g., in SSR)
2023-01-17 22:48:42 -05:00
Greg Johnston
ebeb1d69d1 Merge pull request #334 from leptos-rs/debug-meta
Fix `MetaContext` debug for wasm target
2023-01-17 14:23:41 -05:00
Greg Johnston
cadb04b076 Fix MetaContext debug for wasm target 2023-01-17 14:23:13 -05:00
Greg Johnston
490b7a1596 Merge pull request #332 from leptos-rs/programmatic-navigation-in-router-example
Add programmatic navigation in router example
2023-01-17 13:54:58 -05:00
Greg Johnston
f4d781e739 Merge pull request #331 from benwis/main
Path and Query for Axum
2023-01-17 13:54:49 -05:00
Greg Johnston
ebe5bf4600 Merge pull request #330 from martinfrances107/typed_builder
typed-builder inconsistent version.
2023-01-17 13:53:58 -05:00
Greg Johnston
d62046dc6f Merge pull request #329 from leptos-rs/meta-context-debug
impl `Debug` on `MetaContext`
2023-01-17 13:53:24 -05:00
Greg Johnston
c7abb57168 Merge pull request #328 from martinfrances107/crate_io_readme_issue
Minor: For each sub crate the landing page should be the root README.md.
2023-01-17 13:53:14 -05:00
Greg Johnston
bc0cd5d0ba Minimize panics when runtime has already been disposed (e.g., in streaming SSR) 2023-01-17 13:11:35 -05:00
Greg Johnston
7df67444f9 cargo fmt fix 2023-01-17 12:45:59 -05:00
Greg Johnston
40155e91ea cargo fmt fix 2023-01-17 12:43:27 -05:00
Greg Johnston
5c062fa6f1 Add use_navigate in router example 2023-01-17 12:40:54 -05:00
Greg Johnston
3517820afd Restore missing docs on <A/> component 2023-01-17 12:40:23 -05:00
benwis
300cc4f54c Actually Do It 2023-01-17 09:27:09 -08:00
Martin
586e9be99a Minor - type-builder version is inconsistent. 2023-01-17 17:23:05 +00:00
Greg Johnston
6ed86d0ee9 impl Debug on MetaContext 2023-01-17 12:17:16 -05:00
Martin
1fe93fd588 Minor: For each sub crate the landing page should be the root README.md. 2023-01-17 17:05:09 +00:00
Greg Johnston
2723871a80 Merge pull request #327 from ekanna/main
Updated example code and README to use latest syntax for data binding
2023-01-17 11:56:59 -05:00
benwis
70d92c7f42 Path and Query 2023-01-17 05:52:38 -08:00
Greg Johnston
e96d4b0687 Merge pull request #326 from benwis/main
Make sure Axum returns a relative URI for http and https requests
2023-01-17 06:34:04 -05:00
ekanna
ce0910caca Updated example code and README to use latest syntax for data binding 2023-01-17 12:08:44 +05:30
benwis
81a937277d Simplify URI matching solution 2023-01-16 22:35:22 -08:00
benwis
355e711964 Fix issue with https pathing for Axum integration 2023-01-16 22:18:39 -08:00
Greg Johnston
27ec506fd5 Merge pull request #321 from leptos-rs/for-ssr 2023-01-16 21:49:24 -05:00
Greg Johnston
79c76ae4cb Merge pull request #324 from leptos-rs/fix-fallback
Fix `<Router fallback>` (signature and functionality)
2023-01-16 20:51:01 -05:00
Greg Johnston
e416815591 clippy warning 2023-01-16 20:08:27 -05:00
Greg Johnston
81bdd6788f Fix hydration in release mode if _0-0-0 is a marker, not an element 2023-01-16 20:08:07 -05:00
Greg Johnston
f7d5567a35 Fix fallback (signature and functionality) 2023-01-16 19:55:32 -05:00
Greg Johnston
ae0a243cc0 Fix meta doctests 2023-01-16 12:08:13 -05:00
Greg Johnston
7893ff8b55 Fix SSR doctests 2023-01-16 10:36:18 -05:00
Greg Johnston
6130e708ce cargo fmt 2023-01-16 09:26:40 -05:00
Greg Johnston
d049d2f36b Use comments instead of element markers for hydration -- fixes issue #320 2023-01-16 09:21:28 -05:00
Greg Johnston
61ca6465df Merge pull request #318 from benwis/main
Switch get_configuration calls to None and add a note in the docs for get_configuration()
2023-01-16 07:23:55 -05:00
benwis
10a833d763 Switch get_configuration calls to None and add a note in the docs for
uses of get_configuration()
2023-01-15 12:50:25 -08:00
Greg Johnston
17982e8ac5 Merge pull request #316 from leptos-rs/death-to-suspense-hydration-mismatches
Death to suspense hydration mismatches
2023-01-14 15:19:12 -05:00
Greg Johnston
6cfb6227f5 Merge pull request #315 from leptos-rs/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2023-01-14 14:14:29 -05:00
Greg Johnston
159852b8d7 clippy 2023-01-14 14:12:52 -05:00
Greg Johnston
6f95713b59 Fix <Suspense/> hydration 2023-01-14 14:10:19 -05:00
Greg Johnston
e17afd4559 Handle custom elements correctly 2023-01-14 14:09:23 -05:00
Greg Johnston
e0aa1e245b Create CODE_OF_CONDUCT.md 2023-01-14 12:04:11 -05:00
Greg Johnston
7951a6e9cf Merge pull request #314 from leptos-rs/create-slice-doctest
Fix doctest in `create_slice` and edit doc comment slightly
2023-01-14 09:50:29 -05:00
Greg Johnston
1f39299303 Fix doctest in create_slice and edit doc comment slightly 2023-01-14 08:17:27 -05:00
Greg Johnston
2be4610233 Update README.md 2023-01-14 08:03:57 -05:00
Greg Johnston
af254e9b61 Merge pull request #312 from TaKO8Ki/use-rust-cache
Use rust-cache in CI
2023-01-14 07:59:18 -05:00
Takayuki Maeda
4aae8a5088 use rust cache 2023-01-14 20:07:09 +09:00
Greg Johnston
7ff044cef6 Merge pull request #308 from Indrazar/main
Update Generated API URL on Windows Attempt #2
2023-01-13 07:30:03 -05:00
Greg Johnston
11122b575e Merge pull request #310 from akesson/doc-examples-fixes
Doc fix + search & replace url
2023-01-13 07:29:47 -05:00
Greg Johnston
a62ee4031b Merge branch 'main' into doc-examples-fixes 2023-01-13 07:29:42 -05:00
Greg Johnston
19b43607a1 Merge pull request #309 from dzfrias/patch-1 2023-01-13 07:27:46 -05:00
hakesson
884297706a Search https://github.com/gbj/ and replace with https://github.com/leptos-rs/ 2023-01-13 09:03:11 +01:00
hakesson
fb4c208609 Should point to counters (plural) 2023-01-13 09:00:35 +01:00
Diego Frias
5701e74efb Fix link to counters_stable in README 2023-01-12 20:26:11 -08:00
indrazar
2afe8e202a update url for Windows directories attempt 2 2023-01-12 22:07:55 -05:00
Greg Johnston
08ec473304 Merge pull request #296 from Gentle/create_slice
create_slice in leptos_reactive
2023-01-12 13:26:51 -05:00
Greg Johnston
3ae0880db4 Merge pull request #305 from leptos-rs/0.1.0
Version `0.1.0` release
2023-01-12 12:53:26 -05:00
Ramon Klass
9180aaad7e create_slice: actual documentation 2023-01-12 16:51:37 +01:00
Ramon Klass
89be6bc68e removed Debug from create_slice 2023-01-12 16:30:56 +01:00
Ramon Klass
56426170b0 remove Debug from create_memo 2023-01-12 16:30:56 +01:00
Ramon Klass
0571ea4103 create_slice for RwSignal with 2 closures 2023-01-12 16:30:56 +01:00
Greg Johnston
cdf709fb09 0.1.0 2023-01-12 09:57:08 -05:00
Greg Johnston
7eaa36812d Merge pull request #301 from leptos-rs/ssr-inner-html
Fix SSR of elements with `inner_html`
2023-01-11 21:59:45 -05:00
Greg Johnston
2ef36c65fd cargo fmt 2023-01-11 20:58:26 -05:00
Greg Johnston
a16540ccc5 Merge pull request #302 from martinfrances107/invalid_toml
Minor: "leptos.workspace = true" is invalid.
2023-01-11 20:48:51 -05:00
Greg Johnston
613c7b32a1 Merge pull request #300 from akesson/pre-resolved-data 2023-01-11 13:03:39 -05:00
Martin
8f2a731c9f Minor formatting fix. 2023-01-11 17:44:56 +00:00
Martin
1621b86d8f Minor: "leptos.workspace = true" is invalid. 2023-01-11 17:09:10 +00:00
Greg Johnston
b1ac17995d Fix SSR of elements with inner_html 2023-01-11 07:47:24 -05:00
hakesson
6471af8b89 Let data_fn return DataResponse 2023-01-11 12:08:44 +01:00
hakesson
abf54b832e Improve naming and doc 2023-01-11 09:56:58 +01:00
hakesson
91e839c71a data_fn returns Result 2023-01-11 09:08:07 +01:00
Greg Johnston
b944b17e6d Update Leptos_logo_pref_dark_RGB.svg 2023-01-10 21:56:30 -05:00
Greg Johnston
54c1abb4b7 Merge pull request #298 from leptos-rs/dark-mode-logo
Dark mode logo
2023-01-10 21:52:15 -05:00
Greg Johnston
c24f33aeb2 Dark-mode logo 2023-01-10 21:51:59 -05:00
Greg Johnston
9e83acfe63 Make cargo fmt happy 2023-01-10 21:18:03 -05:00
Greg Johnston
46254a18f3 Improve router feature warning 2023-01-10 21:10:27 -05:00
Greg Johnston
7d7a96d9bc Switch logo on color-scheme change 2023-01-10 13:51:52 -05:00
hakesson
339c920b19 Make data_fn take cloned HttpRequest 2023-01-10 18:55:45 +01:00
Greg Johnston
ecc24fa65d Create FUNDING.yml
Add GitHub sponsor info
2023-01-10 12:52:39 -05:00
hakesson
3f036ee321 Fix refactoring error 2023-01-10 18:48:05 +01:00
hakesson
4e00ec2348 Add leptos_data_routes 2023-01-10 18:27:35 +01:00
hakesson
9c59b720b7 Improve fmt 2023-01-10 18:26:11 +01:00
hakesson
da4340894f Extract fn provide_contexts 2023-01-10 18:18:25 +01:00
hakesson
02f5c3891c Extract fn leptos_corrected_path 2023-01-10 18:06:24 +01:00
hakesson
087e67466f Extract fn html_parts & stream_app 2023-01-10 18:03:11 +01:00
Greg Johnston
1925c5bbe5 Merge pull request #294 from martinfrances107/workflow_cargo_fmt
Policy change ( part 2 ) Added rule of enforcing cargo fmt.
2023-01-10 09:37:38 -05:00
Greg Johnston
1613616008 Merge branch 'main' into workflow_cargo_fmt 2023-01-10 09:36:53 -05:00
Greg Johnston
8a01880ade Merge pull request #293 from benwis/remove-deps
Remove a couple extra deps from the axum integration
2023-01-10 09:36:15 -05:00
Greg Johnston
180ab87ff9 Merge pull request #282 from akesson/workspace-multi-projects
Use envs for workspace config
2023-01-10 09:35:41 -05:00
hakesson
e324fb6e76 Default site-root to '.' 2023-01-10 10:33:53 +01:00
hakesson
0547b4f846 Add missing semi-colon 2023-01-10 10:01:23 +01:00
henrik
75659ce674 Merge branch 'main' into workspace-multi-projects 2023-01-10 09:59:44 +01:00
Martin
190cb162ad Added rustfmt to setup for actions-rs/toolchain. 2023-01-10 08:52:55 +00:00
hakesson
1f556cefb0 Default to serve root 2023-01-10 09:52:21 +01:00
hakesson
6a68ef67f3 Add LEPTOS prefix to OUTPUT_NAME in all files 2023-01-10 09:26:10 +01:00
Martin
23bbd90c81 Policy change ( part 2 ) added rule of enforcing cargo fmt. 2023-01-10 08:20:02 +00:00
benwis
27b8553076 Remove a couple extra deps from the axum integration 2023-01-09 23:46:07 -08:00
hakesson
5bfeb93e3d Make Cargo.toml optional 2023-01-10 08:26:22 +01:00
hakesson
dd9ae1b7b1 Add LEPTOS prefix to OUTPUT_NAME 2023-01-10 08:25:43 +01:00
Greg Johnston
ad34a5d9c6 Update name to leptos-rs/leptos 2023-01-09 21:59:13 -05:00
Greg Johnston
ace5e7cbba Add missing hackernews_axum makefile 2023-01-09 21:59:00 -05:00
Greg Johnston
c8f0988e53 Merge pull request #292 from gbj/router-warnings
Implement `state` and `replace` correctly in `leptos_router` and clear warnings
2023-01-09 21:11:28 -05:00
Greg Johnston
64f0f8879b Implement state and replace correctly in leptos_router and clear warnings 2023-01-09 21:10:42 -05:00
Greg Johnston
b8cafeb650 Merge pull request #289 from gbj/forbid-unsafe
Forbid `unsafe` code in all packages
2023-01-09 20:45:28 -05:00
Greg Johnston
992b218ffe Merge pull request #291 from gbj/correct-axum-query-handling
Correct Axum query handling
2023-01-09 20:44:58 -05:00
Greg Johnston
5df89b0d25 Fix query parsing in Axum integration 2023-01-09 20:44:06 -05:00
Greg Johnston
c050456a47 Use a runtime warning about SVG <a/> instead of a macro warning on all ambiguous tags 2023-01-09 20:31:51 -05:00
Greg Johnston
8a8d7cbe1b Fix forbid_unsafe in a frustratingly-stupid way 2023-01-09 20:07:16 -05:00
Greg Johnston
f5f345e623 Merge pull request #286 from benwis/favicons
FAVICON!
2023-01-09 19:57:27 -05:00
Greg Johnston
4df3687463 Forbid unsafe code in all packages 2023-01-09 19:48:51 -05:00
Greg Johnston
f6622448e9 Update README.md 2023-01-09 19:38:02 -05:00
Greg Johnston
78825401c5 Merge pull request #288 from gbj/new-logo
New logo
2023-01-09 19:34:10 -05:00
Greg Johnston
f2b7ad6244 New logo 2023-01-09 19:33:44 -05:00
Greg Johnston
43f107d9bd New logo 2023-01-09 19:33:38 -05:00
Greg Johnston
a2612ca1fc Merge pull request #275 from martinfrances107/cargo_fmt
Policy change: Workflow now enforce "cargo fmt".
2023-01-09 19:20:57 -05:00
Greg Johnston
a000c84e1a Fix scope disposal code in Router (closes issue #240) 2023-01-09 19:17:37 -05:00
benwis
ee647cba1c Add Favicons to all the examples and standardize on the public folder for public assets 2023-01-09 15:27:52 -08:00
Greg Johnston
1377b823e2 Merge pull request #285 from martinfrances107/version_numbers
simple_logger use version 4.0.0 everywhere.
2023-01-09 18:26:57 -05:00
Martin
4d21f5ac63 simple_logger use version 4.0.0 everywhere. 2023-01-09 21:43:48 +00:00
hakesson
1ec603ee58 Use envs for workspace config 2023-01-09 20:06:43 +01:00
Martin
c56806713e Keeping up with changes to main. 2023-01-09 12:47:50 +00:00
Martin
2f6aa6753d Removed workflow "Cargo fmt" test. 2023-01-09 12:44:35 +00:00
Martin
2544687acd Moved the order of check. 2023-01-09 12:44:35 +00:00
Martin
3d25e86c23 Policy change: Workflow now enforce "cargo fmt". 2023-01-09 12:44:30 +00:00
Greg Johnston
28ec3a6cda Merge pull request #281 from martinfrances107/hackernews_axum
Minor: Clippy fixes related to hackernew_axum.
2023-01-09 07:02:43 -05:00
Greg Johnston
8b92a561a3 Merge pull request #269 from benwis/generated_routes
Generate Routes and pass them to Actix/Axum
2023-01-09 07:02:11 -05:00
Martin
e490c0423f Minor: Clippy fixes related to hackernew_axum. 2023-01-09 09:54:39 +00:00
benwis
b6579a040a Warning Squashing 2023-01-08 19:41:22 -08:00
benwis
01e024b726 One more time! 2023-01-08 19:38:45 -08:00
benwis
6603c44ce2 Minor fixes and revisions 2023-01-08 19:36:24 -08:00
Greg Johnston
977f11b180 Merge pull request #280 from gbj/prevent-panic-in-ssr
Closes #278: Prevent `create_signal_from_stream` from panicking on SSR
2023-01-08 19:52:48 -05:00
Greg Johnston
fb34b29ccf Merge pull request #279 from DPM97/default_prop_values
default prop values in prop macro
2023-01-08 19:52:38 -05:00
benwis
6576d8eda1 Change site_root default to ".". Hope that cargo-leptos has that check 2023-01-08 14:54:32 -08:00
benwis
6b729f9131 Fix hackernews_axum example 2023-01-08 14:51:41 -08:00
benwis
dc60c35b58 Rewrite file handlers for Axum, and update all examples to use the new
generated routes. Fix a few issues in the integrations, and reduce the
number of warnings
2023-01-08 14:18:51 -08:00
Greg Johnston
32ec9cc57e Prevent create_signal_from_stream from panicking on SSR 2023-01-08 17:09:04 -05:00
Dylan Maloy
35601d8284 update abort_opt_message 2023-01-08 16:47:02 -05:00
Dylan Maloy
49bc7d2a27 init 2023-01-08 16:35:43 -05:00
Greg Johnston
aa7c7367dc Merge pull request #277 from martinfrances107/clippy_hackernews
examples/hackernews - Cargo clippy fixes.
2023-01-08 14:10:57 -05:00
Greg Johnston
70808c5262 Merge pull request #272 from DPM97/component_lifetimes
fix component macro lifetime parsing
2023-01-08 14:07:00 -05:00
Martin
67503a108d BugFix. 2023-01-08 15:28:30 +00:00
Martin
ef52a01838 examples/hackernews - Cargo clippy fixes. 2023-01-08 13:33:53 +00:00
Greg Johnston
4cacfe98d8 Merge pull request #273 from martinfrances107/needless_borrow
Minor: Removed Clippy::needless_borrrow issues.
2023-01-08 07:27:02 -05:00
Greg Johnston
52e653316e Merge pull request #270 from gbj/chorse
Chores
2023-01-08 07:26:14 -05:00
Greg Johnston
bf5b6ca9c2 Merge pull request #264 from martinfrances107/Clipp_removed_clone_round_2
Removing clone call where possible(round2).
2023-01-08 07:26:00 -05:00
Martin
8875939a27 Minor: Removed Clippy::needless_borrrow issues. 2023-01-08 10:42:25 +00:00
Martin
b9a83277d9 Removing clone call where possible(round2). 2023-01-08 09:57:19 +00:00
benwis
dad84b5867 Merge branch 'main' into generated_routes 2023-01-07 21:17:18 -08:00
Dylan Maloy
1f29d29947 init 2023-01-08 00:15:00 -05:00
benwis
1b8175e2fa Add missing tokio dep for RwLock 2023-01-07 20:27:05 -08:00
Greg Johnston
dbe3ec015c Fix warning for unused variable. 2023-01-07 22:06:02 -05:00
Greg Johnston
492fa6c6d3 Fix link to Stream in docs. 2023-01-07 22:05:44 -05:00
Greg Johnston
343e8c8abe Update macro docs to reflect newly-available class syntax. 2023-01-07 22:02:23 -05:00
Greg Johnston
656d20cb65 Don't panic in proc macro, use proc_macro_error instead. 2023-01-07 21:32:52 -05:00
Greg Johnston
a0a66b75dd Allow complex class names with ("class-[name]-42", value) syntax. 2023-01-07 21:29:26 -05:00
Greg Johnston
085ba3506c Merge pull request #267 from gbj/adjust-tracing
Adjust tracing
2023-01-07 20:31:54 -05:00
Greg Johnston
63f3780eda Merge pull request #268 from gbj/fnonce-children
`children` should take `FnOnce(Scope) -> Fragment`, to ease need of c…
2023-01-07 20:31:29 -05:00
benwis
c41cf879d1 Formatting 2023-01-07 15:44:35 -08:00
benwis
b34f2070d3 Remove extraneous route 2023-01-07 15:09:49 -08:00
benwis
7fa21defa6 Merge remote-tracking branch 'origin/generated_routes' into generated_routes 2023-01-07 15:08:12 -08:00
benwis
bdd9abc04d Removing some missed code and changing the stylesheet 2023-01-07 15:06:21 -08:00
Ben Wishovich
1d25134213 Merge branch 'main' into generated_routes 2023-01-07 14:58:26 -08:00
benwis
de73622949 Change Axum's "" matching 2023-01-07 14:53:38 -08:00
benwis
5d3cfc6483 Actix seems to be working now, plus applied Henrik's path recommendations 2023-01-07 14:49:25 -08:00
Greg Johnston
6fbbd09000 <Suspense/> and <Transition/> should take Fn for children because they need to use it multiple times 2023-01-07 17:09:50 -05:00
Greg Johnston
f2842cf14e children should take FnOnce(Scope) -> Fragment, to ease need of cloning etc. 2023-01-07 17:04:58 -05:00
benwis
677e4f2540 Leptos can now generate routes and provide them to the Axum router. More
testing and Actix version to come
2023-01-06 19:52:38 -08:00
benwis
63b1837315 First pass of method to generate routelist 2023-01-06 14:08:45 -08:00
192 changed files with 4781 additions and 1846 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: gbj

View File

@@ -28,6 +28,7 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt
- name: Setup cargo-make
uses: davidB/rust-cargo-make@v1
@@ -35,16 +36,10 @@ jobs:
- name: Cargo generate-lockfile
run: cargo generate-lockfile
- name: Cargo cache
uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ matrix.rust }}-${{ hashFiles('**/Cargo.lock') }}
- name: Run Rustfmt
run: cargo fmt -- --check
- uses: Swatinem/rust-cache@v2
- name: Run tests with all features
run: cargo make ci

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ blob.rs
Cargo.lock
**/*.rs.bk
.DS_Store
.idea

51
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,51 @@
# Contributor Covenant Code of Conduct
_This Code of Conduct is based on the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)
and the [Bevy Code of Conduct](https://raw.githubusercontent.com/bevyengine/bevy/main/CODE_OF_CONDUCT.md),
which are adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling)
and the [Contributor Covenant](https://www.contributor-covenant.org)._
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
We are a community of people learning and exploring how to build better web applications
with Rust. When interacting with one another, please remember that there are no experts and there are
no stupid questions. Assume the best in other people's communication, and take a step back if
you find yourself getting defensive.
Please note the following guidelines as well:
* Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.
* Please be kind and courteous. Theres no need to be mean or rude.
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](https://github.com/stumpsyn/policies/blob/master/citizen_code_of_conduct.md); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we dont tolerate behavior that excludes people in socially marginalized groups.
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact the maintainers immediately. Whether youre a regular contributor or a newcomer, we care about making this community a safe place for you and weve got your back.
* Do not make casual mention of slavery or indentured servitude and/or false comparisons of one's occupation or situation to slavery. Please consider using or asking about alternate terminology when referring to such metaphors in technology.
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
## Moderation
These are the policies for upholding [our communitys standards of conduct](#our-standards). If you feel that a thread needs moderation, please contact the maintainers.
1. Remarks that violate the community standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner).
2. Remarks that maintainers find inappropriate, whether listed in the code of conduct or not, are also not allowed.
3. Maintainers will first respond to such remarks with a warning.
4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
6. Maintainers may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
7. If a maintainer bans someone and you think it was unjustified, please take it up with that maintainer, or with a different maintainer, in private. Complaints about bans in-channel are not allowed.
8. Maintainers are held to a higher standard than other community members. If a maintainer creates an inappropriate situation, they should expect less leeway than others.
The enforcement policies in the code of conduct apply to all official venues, including Discord channels, GitHub repositories, and all other forums.

View File

@@ -24,17 +24,17 @@ members = [
exclude = ["benchmarks", "examples"]
[workspace.package]
version = "0.1.0-beta"
version = "0.1.3"
[workspace.dependencies]
leptos = { path = "./leptos", default-features = false, version = "0.1.0-beta" }
leptos_dom = { path = "./leptos_dom", default-features = false, version = "0.1.0-beta" }
leptos_macro = { path = "./leptos_macro", default-features = false, version = "0.1.0-beta" }
leptos_reactive = { path = "./leptos_reactive", default-features = false, version = "0.1.0-beta" }
leptos_server = { path = "./leptos_server", default-features = false, version = "0.1.0-beta" }
leptos_config = { path = "./leptos_config", default-features = false, version = "0.1.0-beta" }
leptos_router = { path = "./router", version = "0.1.0-beta" }
leptos_meta = { path = "./meta", default-feature = false, version = "0.1.0-beta" }
leptos = { path = "./leptos", default-features = false, version = "0.1.3" }
leptos_dom = { path = "./leptos_dom", default-features = false, version = "0.1.3" }
leptos_macro = { path = "./leptos_macro", default-features = false, version = "0.1.3" }
leptos_reactive = { path = "./leptos_reactive", default-features = false, version = "0.1.3" }
leptos_server = { path = "./leptos_server", default-features = false, version = "0.1.3" }
leptos_config = { path = "./leptos_config", default-features = false, version = "0.1.3" }
leptos_router = { path = "./router", version = "0.1.3" }
leptos_meta = { path = "./meta", default-feature = false, version = "0.1.3" }
[profile.release]
codegen-units = 1

View File

@@ -1,6 +1,7 @@
**NOTE: We're in the middle of merging changes and making fixes to support our upcoming `0.1.0` release. Some of the examples may be in a broken state. You can continue using the `0.0` releases with no issues.**
<img src="https://raw.githubusercontent.com/gbj/leptos/main/docs/logos/logo.svg" alt="Leptos Logo" style="width: 100%; height: auto; display: block; margin: auto;">
<picture>
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_pref_dark_RGB.svg" media="(prefers-color-scheme: dark)">
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
</picture>
[![crates.io](https://img.shields.io/crates/v/leptos.svg)](https://crates.io/crates/leptos)
[![docs.rs](https://docs.rs/leptos/badge.svg)](https://docs.rs/leptos)
@@ -28,7 +29,7 @@ pub fn SimpleCounter(cx: Scope, initial_value: i32) -> impl IntoView {
<div>
<button on:click=clear>"Clear"</button>
<button on:click=decrement>"-1"</button>
<span>"Value: " {move || value().to_string()} "!"</span>
<span>"Value: " {value} "!"</span>
<button on:click=increment>"+1"</button>
</div>
}
@@ -58,12 +59,11 @@ Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained re
Here are some resources for learning more about Leptos:
- [Examples](https://github.com/gbj/leptos/tree/main/examples)
- [Examples](https://github.com/leptos-rs/leptos/tree/main/examples)
- [API Documentation](https://docs.rs/leptos/latest/leptos/)
- [Common Bugs](https://github.com/gbj/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
- [Common Bugs](https://github.com/leptos-rs/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
- Leptos Guide (in progress)
## `nightly` Note
Most of the examples assume youre using `nightly` Rust.
@@ -81,12 +81,12 @@ If youre on `stable`, note the following:
1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.1.0-alpha", features = ["stable"] }`
2. `nightly` enables the function call syntax for accessing and setting signals. If youre using `stable`,
youll just call `.get()`, `.set()`, or `.update()` manually. Check out the
[`counters-stable` example](https://github.com/gbj/leptos/blob/main/examples/counters-stable/src/main.rs)
[`counters_stable` example](https://github.com/leptos-rs/leptos/blob/main/examples/counters_stable/src/main.rs)
for examples of the correct API.
## `cargo-leptos`
[`cargo-leptos`](https://github.com/akesson/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our [starter template](https://github.com/leptos-rs/start).
[`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our [starter template](https://github.com/leptos-rs/start).
```bash
cargo install cargo-leptos
@@ -95,8 +95,28 @@ cd [your project name]
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
## FAQs
### Is it production ready?
People usually mean one of three things by this question.
1. **Are the APIs stable?** i.e., will I have to rewrite my whole app from Leptos 0.1 to 0.2 to 0.3 to 0.4, or can I write it now and benefit from new features and updates as new versions come?
With 0.1 the APIs are basically settled. Were adding new features, but were very happy with where the type system and patterns have landed. I would not expect major breaking changes to your code to adapt to, for example, a 0.2.0 release.
2. **Are there bugs?**
Yes, Im sure there are. You can see from the state of our issue tracker over time that there arent that _many_ bugs and theyre usually resolved pretty quickly. But for sure, there may be moments where you encounter something that requires a fix at the framework level, which may not be immediately resolved.
3. **Am I a consumer or a contributor?**
This may be the big one: “production ready” implies a certain orientation to a library: that you can basically use it, without any special knowledge of its internals or ability to contribute. Everyone has this at some level in their stack: for example I (@gbj) dont have the capacity or knowledge to contribute to something like `wasm-bindgen` at this point: I simply rely on it to work.
There are several people in this community using Leptos right now for internal apps at work, who have also become significant contributors. I think this is the right level of production use for now. There may be missing features that you need, and you may end up building them! But for internal apps, if youre willing to build and contribute missing pieces along the way, the framework is definitely usable right now.
### Can I use this for native GUI?
Sure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive native any GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:
@@ -106,7 +126,7 @@ Sure! Obviously the `view` macro is for generating DOM nodes but you can use the
- Use event listeners to update signals
- Create effects to update the UI
I've put together a [very simple GTK example](https://github.com/gbj/leptos/blob/main/examples/gtk/src/main.rs) so you can see what I mean.
I've put together a [very simple GTK example](https://github.com/leptos-rs/leptos/blob/main/examples/gtk/src/main.rs) so you can see what I mean.
### How is this different from Yew/Dioxus?
@@ -124,7 +144,6 @@ There are some practical differences that make a significant difference:
- **Maturity:** Sycamore is obviously a much more mature and stable library with a larger ecosystem.
- **Templating:** Leptos uses a JSX-like template format (built on [syn-rsx](https://github.com/stoically/syn-rsx)) for its `view` macro. Sycamore offers the choice of its own templating DSL or a builder syntax.
- **Template node cloning:** Leptos's `view` macro compiles to a static HTML string and a set of instructions of how to assign its reactive values. This means that at runtime, Leptos can clone a `<template>` node rather than calling `document.createElement()` to create DOM nodes. This is a _significantly_ faster way of rendering components.
- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use `create_rw_signal` to give a unified read/write signal.)_
- **Signals are functions:** In Leptos, you can call a signal to access it rather than calling a specific method (so, `count()` instead of `count.get()`) This creates a more consistent mental model: accessing a reactive value is always a matter of calling a function. For example:

View File

@@ -2,9 +2,9 @@ use test::Bencher;
#[bench]
fn leptos_ssr_bench(b: &mut Bencher) {
b.iter(|| {
b.iter(|| {
use leptos::*;
HydrationCtx::reset_id();
_ = create_scope(create_runtime(), |cx| {
#[component]
fn Counter(cx: Scope, initial: i32) -> impl IntoView {
@@ -32,18 +32,17 @@ fn leptos_ssr_bench(b: &mut Bencher) {
assert_eq!(
rendered,
"<main><h1>Welcome to our benchmark page.</h1><p>Here's some introductory text.</p><div><button>-1</button><span>Value: <!>1<template id=\"_3\"></template>!</span><button>+1</button></div><template id=\"_1\"></template><div><button>-1</button><span>Value: <!>2<template id=\"_2\"></template>!</span><button>+1</button></div><template id=\"_0\"></template><div><button>-1</button><span>Value: <!>3<template id=\"_2\"></template>!</span><button>+1</button></div><template id=\"_0\"></template></main>"
);
"<main id=\"_0-1\"><h1 id=\"_0-2\">Welcome to our benchmark page.</h1><p id=\"_0-3\">Here's some introductory text.</p><div id=\"_0-3-1\"><button id=\"_0-3-2\">-1</button><span id=\"_0-3-3\">Value: <!>1<!--hk=_0-3-4-->!</span><button id=\"_0-3-5\">+1</button></div><!--hk=_0-3-0--><div id=\"_0-3-5-1\"><button id=\"_0-3-5-2\">-1</button><span id=\"_0-3-5-3\">Value: <!>2<!--hk=_0-3-5-4-->!</span><button id=\"_0-3-5-5\">+1</button></div><!--hk=_0-3-5-0--><div id=\"_0-3-5-5-1\"><button id=\"_0-3-5-5-2\">-1</button><span id=\"_0-3-5-5-3\">Value: <!>3<!--hk=_0-3-5-5-4-->!</span><button id=\"_0-3-5-5-5\">+1</button></div><!--hk=_0-3-5-5-0--></main>" );
});
});
}
/*
#[bench]
fn tera_ssr_bench(b: &mut Bencher) {
use tera::*;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use tera::*;
static TEMPLATE: &str = r#"<main>
static TEMPLATE: &str = r#"<main>
<h1>Welcome to our benchmark page.</h1>
<p>Here's some introductory text.</p>
{% for counter in counters %}
@@ -55,37 +54,40 @@ fn tera_ssr_bench(b: &mut Bencher) {
{% endfor %}
</main>"#;
lazy_static::lazy_static! {
static ref TERA: Tera = {
let mut tera = Tera::default();
tera.add_raw_templates(vec![("template.html", TEMPLATE)]).unwrap();
tera
};
}
lazy_static::lazy_static! {
static ref TERA: Tera = {
let mut tera = Tera::default();
tera.add_raw_templates(vec![("template.html", TEMPLATE)]).unwrap();
tera
};
}
#[derive(Serialize, Deserialize)]
struct Counter {
value: i32
}
#[derive(Serialize, Deserialize)]
struct Counter {
value: i32,
}
b.iter(|| {
let mut ctx = Context::new();
ctx.insert("counters", &vec![
Counter { value: 0 },
Counter { value: 1},
Counter { value: 2 }
]);
b.iter(|| {
let mut ctx = Context::new();
ctx.insert(
"counters",
&vec![
Counter { value: 0 },
Counter { value: 1 },
Counter { value: 2 },
],
);
let _ = TERA.render("template.html", &ctx).unwrap();
});
let _ = TERA.render("template.html", &ctx).unwrap();
});
}
#[bench]
fn sycamore_ssr_bench(b: &mut Bencher) {
use sycamore::*;
use sycamore::prelude::*;
use sycamore::prelude::*;
use sycamore::*;
b.iter(|| {
b.iter(|| {
_ = create_scope(|cx| {
#[derive(Prop)]
struct CounterProps {
@@ -139,10 +141,10 @@ fn sycamore_ssr_bench(b: &mut Bencher) {
#[bench]
fn yew_ssr_bench(b: &mut Bencher) {
use yew::prelude::*;
use yew::ServerRenderer;
use yew::prelude::*;
use yew::ServerRenderer;
b.iter(|| {
b.iter(|| {
#[derive(Properties, PartialEq, Eq, Debug)]
struct CounterProps {
initial: i32
@@ -194,4 +196,3 @@ fn yew_ssr_bench(b: &mut Bencher) {
});
});
}
*/

View File

@@ -8,171 +8,165 @@ pub struct Todos(pub Vec<Todo>);
const STORAGE_KEY: &str = "todos-leptos";
impl Todos {
pub fn new(cx: Scope) -> Self {
Self(vec![])
}
pub fn new(cx: Scope) -> Self {
Self(vec![])
}
pub fn new_with_1000(cx: Scope) -> Self {
let todos = (0..1000)
.map(|id| Todo::new(cx, id, format!("Todo #{id}")))
.collect();
Self(todos)
}
pub fn new_with_1000(cx: Scope) -> Self {
let todos = (0..1000)
.map(|id| Todo::new(cx, id, format!("Todo #{id}")))
.collect();
Self(todos)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn add(&mut self, todo: Todo) {
self.0.push(todo);
}
pub fn add(&mut self, todo: Todo) {
self.0.push(todo);
}
pub fn remove(&mut self, id: usize) {
self.0.retain(|todo| todo.id != id);
}
pub fn remove(&mut self, id: usize) {
self.0.retain(|todo| todo.id != id);
}
pub fn remaining(&self) -> usize {
self.0.iter().filter(|todo| !(todo.completed)()).count()
}
pub fn remaining(&self) -> usize {
self.0.iter().filter(|todo| !(todo.completed)()).count()
}
pub fn completed(&self) -> usize {
self.0.iter().filter(|todo| (todo.completed)()).count()
}
pub fn completed(&self) -> usize {
self.0.iter().filter(|todo| (todo.completed)()).count()
}
pub fn toggle_all(&self) {
// if all are complete, mark them all active instead
if self.remaining() == 0 {
for todo in &self.0 {
if todo.completed.get() {
(todo.set_completed)(false);
pub fn toggle_all(&self) {
// if all are complete, mark them all active instead
if self.remaining() == 0 {
for todo in &self.0 {
if todo.completed.get() {
(todo.set_completed)(false);
}
}
}
// otherwise, mark them all complete
else {
for todo in &self.0 {
(todo.set_completed)(true);
}
}
}
}
// otherwise, mark them all complete
else {
for todo in &self.0 {
(todo.set_completed)(true);
}
}
}
fn clear_completed(&mut self) {
self.0.retain(|todo| !todo.completed.get());
}
fn clear_completed(&mut self) {
self.0.retain(|todo| !todo.completed.get());
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Todo {
pub id: usize,
pub title: ReadSignal<String>,
pub set_title: WriteSignal<String>,
pub completed: ReadSignal<bool>,
pub set_completed: WriteSignal<bool>,
pub id: usize,
pub title: ReadSignal<String>,
pub set_title: WriteSignal<String>,
pub completed: ReadSignal<bool>,
pub set_completed: WriteSignal<bool>,
}
impl Todo {
pub fn new(cx: Scope, id: usize, title: String) -> Self {
Self::new_with_completed(cx, id, title, false)
}
pub fn new_with_completed(
cx: Scope,
id: usize,
title: String,
completed: bool,
) -> Self {
let (title, set_title) = create_signal(cx, title);
let (completed, set_completed) = create_signal(cx, completed);
Self {
id,
title,
set_title,
completed,
set_completed,
pub fn new(cx: Scope, id: usize, title: String) -> Self {
Self::new_with_completed(cx, id, title, false)
}
}
pub fn toggle(&self) {
self
.set_completed
.update(|completed| *completed = !*completed);
}
pub fn new_with_completed(cx: Scope, id: usize, title: String, completed: bool) -> Self {
let (title, set_title) = create_signal(cx, title);
let (completed, set_completed) = create_signal(cx, completed);
Self {
id,
title,
set_title,
completed,
set_completed,
}
}
pub fn toggle(&self) {
self.set_completed
.update(|completed| *completed = !*completed);
}
}
const ESCAPE_KEY: u32 = 27;
const ENTER_KEY: u32 = 13;
#[component]
pub fn TodoMVC(cx: Scope,todos: Todos) -> impl IntoView {
let mut next_id = todos
.0
.iter()
.map(|todo| todo.id)
.max()
.map(|last| last + 1)
.unwrap_or(0);
let (todos, set_todos) = create_signal(cx, todos);
provide_context(cx, set_todos);
let (mode, set_mode) = create_signal(cx, Mode::All);
window_event_listener("hashchange", move |_| {
let new_mode = location_hash().map(|hash| route(&hash)).unwrap_or_default();
set_mode(new_mode);
});
let add_todo = move |ev: web_sys::KeyboardEvent| {
let target = event_target::<HtmlInputElement>(&ev);
ev.stop_propagation();
let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();
if key_code == ENTER_KEY {
let title = event_target_value(&ev);
let title = title.trim();
if !title.is_empty() {
let new = Todo::new(cx, next_id, title.to_string());
set_todos.update(|t| t.add(new));
next_id += 1;
target.set_value("");
}
}
};
let filtered_todos = create_memo::<Vec<Todo>>(cx, move |_| {
todos.with(|todos| match mode.get() {
Mode::All => todos.0.to_vec(),
Mode::Active => todos
pub fn TodoMVC(cx: Scope, todos: Todos) -> impl IntoView {
let mut next_id = todos
.0
.iter()
.filter(|todo| !todo.completed.get())
.cloned()
.collect(),
Mode::Completed => todos
.0
.iter()
.filter(|todo| todo.completed.get())
.cloned()
.collect(),
})
});
.map(|todo| todo.id)
.max()
.map(|last| last + 1)
.unwrap_or(0);
// effect to serialize to JSON
// this does reactive reads, so it will automatically serialize on any relevant change
create_effect(cx, move |_| {
if let Ok(Some(storage)) = window().local_storage() {
let objs = todos
.get()
.0
.iter()
.map(TodoSerialized::from)
.collect::<Vec<_>>();
let json = json::to_string(&objs);
if storage.set_item(STORAGE_KEY, &json).is_err() {
log::error!("error while trying to set item in localStorage");
}
}
});
let (todos, set_todos) = create_signal(cx, todos);
provide_context(cx, set_todos);
view! { cx,
let (mode, set_mode) = create_signal(cx, Mode::All);
window_event_listener("hashchange", move |_| {
let new_mode = location_hash().map(|hash| route(&hash)).unwrap_or_default();
set_mode(new_mode);
});
let add_todo = move |ev: web_sys::KeyboardEvent| {
let target = event_target::<HtmlInputElement>(&ev);
ev.stop_propagation();
let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();
if key_code == ENTER_KEY {
let title = event_target_value(&ev);
let title = title.trim();
if !title.is_empty() {
let new = Todo::new(cx, next_id, title.to_string());
set_todos.update(|t| t.add(new));
next_id += 1;
target.set_value("");
}
}
};
let filtered_todos = create_memo::<Vec<Todo>>(cx, move |_| {
todos.with(|todos| match mode.get() {
Mode::All => todos.0.to_vec(),
Mode::Active => todos
.0
.iter()
.filter(|todo| !todo.completed.get())
.cloned()
.collect(),
Mode::Completed => todos
.0
.iter()
.filter(|todo| todo.completed.get())
.cloned()
.collect(),
})
});
// effect to serialize to JSON
// this does reactive reads, so it will automatically serialize on any relevant change
create_effect(cx, move |_| {
if let Ok(Some(storage)) = window().local_storage() {
let objs = todos
.get()
.0
.iter()
.map(TodoSerialized::from)
.collect::<Vec<_>>();
let json = json::to_string(&objs);
if storage.set_item(STORAGE_KEY, &json).is_err() {
log::error!("error while trying to set item in localStorage");
}
}
});
view! { cx,
<main>
<section class="todoapp">
<header class="header">
@@ -228,100 +222,100 @@ pub fn TodoMVC(cx: Scope,todos: Todos) -> impl IntoView {
#[component]
pub fn Todo(cx: Scope, todo: Todo) -> impl IntoView {
let (editing, set_editing) = create_signal(cx, false);
let set_todos = use_context::<WriteSignal<Todos>>(cx).unwrap();
//let input = NodeRef::new(cx);
let (editing, set_editing) = create_signal(cx, false);
let set_todos = use_context::<WriteSignal<Todos>>(cx).unwrap();
//let input = NodeRef::new(cx);
let save = move |value: &str| {
let value = value.trim();
if value.is_empty() {
set_todos.update(|t| t.remove(todo.id));
} else {
(todo.set_title)(value.to_string());
let save = move |value: &str| {
let value = value.trim();
if value.is_empty() {
set_todos.update(|t| t.remove(todo.id));
} else {
(todo.set_title)(value.to_string());
}
set_editing(false);
};
view! { cx,
<li
class="todo"
class:editing={editing}
class:completed={move || (todo.completed)()}
//_ref=input
>
<div class="view">
<input
class="toggle"
type="checkbox"
prop:checked={move || (todo.completed)()}
/>
<label on:dblclick=move |_| set_editing(true)>
{move || todo.title.get()}
</label>
<button class="destroy" on:click=move |_| set_todos.update(|t| t.remove(todo.id))/>
</div>
{move || editing().then(|| view! { cx,
<input
class="edit"
class:hidden={move || !(editing)()}
prop:value={move || todo.title.get()}
on:focusout=move |ev| save(&event_target_value(&ev))
on:keyup={move |ev| {
let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();
if key_code == ENTER_KEY {
save(&event_target_value(&ev));
} else if key_code == ESCAPE_KEY {
set_editing(false);
}
}}
/>
})
}
</li>
}
set_editing(false);
};
view! { cx,
<li
class="todo"
class:editing={editing}
class:completed={move || (todo.completed)()}
//_ref=input
>
<div class="view">
<input
class="toggle"
type="checkbox"
prop:checked={move || (todo.completed)()}
/>
<label on:dblclick=move |_| set_editing(true)>
{move || todo.title.get()}
</label>
<button class="destroy" on:click=move |_| set_todos.update(|t| t.remove(todo.id))/>
</div>
{move || editing().then(|| view! { cx,
<input
class="edit"
class:hidden={move || !(editing)()}
prop:value={move || todo.title.get()}
on:focusout=move |ev| save(&event_target_value(&ev))
on:keyup={move |ev| {
let key_code = ev.unchecked_ref::<web_sys::KeyboardEvent>().key_code();
if key_code == ENTER_KEY {
save(&event_target_value(&ev));
} else if key_code == ESCAPE_KEY {
set_editing(false);
}
}}
/>
})
}
</li>
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode {
Active,
Completed,
All,
Active,
Completed,
All,
}
impl Default for Mode {
fn default() -> Self {
Mode::All
}
fn default() -> Self {
Mode::All
}
}
pub fn route(hash: &str) -> Mode {
match hash {
"/active" => Mode::Active,
"/completed" => Mode::Completed,
_ => Mode::All,
}
match hash {
"/active" => Mode::Active,
"/completed" => Mode::Completed,
_ => Mode::All,
}
}
#[derive(Serialize, Deserialize)]
pub struct TodoSerialized {
pub id: usize,
pub title: String,
pub completed: bool,
pub id: usize,
pub title: String,
pub completed: bool,
}
impl TodoSerialized {
pub fn into_todo(self, cx: Scope) -> Todo {
Todo::new_with_completed(cx, self.id, self.title, self.completed)
}
pub fn into_todo(self, cx: Scope) -> Todo {
Todo::new_with_completed(cx, self.id, self.title, self.completed)
}
}
impl From<&Todo> for TodoSerialized {
fn from(todo: &Todo) -> Self {
Self {
id: todo.id,
title: todo.title.get(),
completed: (todo.completed)(),
fn from(todo: &Todo) -> Self {
Self {
id: todo.id,
title: todo.title.get(),
completed: (todo.completed)(),
}
}
}
}

View File

@@ -14,7 +14,9 @@ fn leptos_todomvc_ssr(b: &mut Bencher) {
let rendered = view! {
cx,
<TodoMVC todos=Todos::new(cx)/>
}.into_view(cx).render_to_string(cx);
}
.into_view(cx)
.render_to_string(cx);
assert!(rendered.len() > 1);
});
@@ -55,7 +57,7 @@ fn yew_todomvc_ssr(b: &mut Bencher) {
});
});
}
/*
#[bench]
fn leptos_todomvc_ssr_with_1000(b: &mut Bencher) {
b.iter(|| {
@@ -107,3 +109,4 @@ fn yew_todomvc_ssr_with_1000(b: &mut Bencher) {
});
});
}
*/

View File

@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
leptos = "0.0.18"
leptos = "0.1"

View File

@@ -1,5 +1,5 @@
use leptos::*;
fn main() {
mount_to_body(|_cx| view! { cx, <p>"Hello, world!"</p> })
mount_to_body(|cx| view! { cx, <p>"Hello, world!"</p> })
}

View File

@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
leptos = "0.0.18"
leptos = "0.1"

View File

@@ -4,7 +4,9 @@ fn main() {
mount_to_body(|cx| {
let name = "gbj";
let userid = 0;
let _input_element: Element;
// This will be filled by _ref=input below.
let input_element = NodeRef::<HtmlElement<Input>>::new(cx);
view! {
cx,
@@ -17,7 +19,7 @@ fn main() {
prop:value="todo" // `prop:` lets you set a property on a DOM node
value="initial" // side note: the DOM `value` attribute only sets *initial* value
// this is very important when working with forms!
_ref=_input_element // `_ref` stores tis element in a variable
_ref=input_element // `_ref` stores tis element in a variable
/>
<ul data-user=userid> // attributes can take expressions as values
<li class="todo my-todo" // here we set the `class` attribute

View File

@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
leptos = "0.0.18"
leptos = "0.1"

View File

@@ -1,6 +1,6 @@
# Introduction
This book is intended as an introduction to the [Leptos](https://github.com/gbj/leptos) Web framework. Together, well build a simple todo app—first as a client-side app, then as a full-stack app.
This book is intended as an introduction to the [Leptos](https://github.com/leptos-rs/leptos) Web framework. Together, well build a simple todo app—first as a client-side app, then as a full-stack app.
The guide doesnt assume you know anything about fine-grained reactivity or the details of modern Web frameworks. It does assume you are familiar with the Rust programming language, HTML, CSS, and the DOM and other Web APIs.

View File

@@ -1,8 +1,8 @@
# Getting Started
> The code for this chapter can be found [here](https://github.com/gbj/leptos/tree/main/docs/book/project/ch02_getting_started).
> The code for this chapter can be found [here](https://github.com/leptos-rs/leptos/tree/main/docs/book/project/ch02_getting_started).
The easiest way to get started using Leptos is to use [Trunk](https://trunkrs.dev/), as many of our [examples](https://github.com/gbj/leptos/tree/main/examples) do. (Trunk is a simple build tool that includes a dev server.)
The easiest way to get started using Leptos is to use [Trunk](https://trunkrs.dev/), as many of our [examples](https://github.com/leptos-rs/leptos/tree/main/examples) do. (Trunk is a simple build tool that includes a dev server.)
If you dont already have it installed, you can install Trunk by running

View File

@@ -1,6 +1,6 @@
# Templating: Building User Interfaces
> The code for this chapter can be found [here](https://github.com/gbj/leptos/tree/main/docs/book/project/ch03_building_ui).
> The code for this chapter can be found [here](https://github.com/leptos-rs/leptos/tree/main/docs/book/project/ch03_building_ui).
## RSX and the `view!` macro

BIN
docs/logos/Leptos_logo_RGB.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

64
docs/logos/Leptos_logo_RGB.svg Executable file
View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 437.4294 209.6185" style="enable-background:new 0 0 437.4294 209.6185;" xml:space="preserve">
<path style="fill:none;" d="M130.0327,79.3931c-11.4854-0.23-22.52,9.3486-24.5034,21.0117l49.1157,0.0293
c-2.1729-10.418-11.1821-21.0449-24.1987-21.0449C130.3081,79.3892,130.1714,79.3907,130.0327,79.3931z"/>
<path style="fill:#181139;" d="M95.1109,128.1089H58.6797V65.6861c0-1.5234-0.8169-2.4331-2.1855-2.4331h-3.1187
c-1.3159,0-2.2349,1.0005-2.2349,2.4331v67.4297c0,1.4521,0.8145,2.2852,2.2349,2.2852h41.7353c1.4844,0,2.4819-0.9375,2.4819-2.333
v-2.7744C97.5928,128.9253,96.6651,128.1089,95.1109,128.1089z"/>
<path style="fill:#181139;" d="M146.4561,77.1739c-4.8252-3.001-10.3037-4.5249-16.2837-4.5288c-0.0068,0-0.0137,0-0.0205,0
c-5.7349,0-11.1377,1.4639-16.0566,4.3511c-4.916,2.8853-8.8721,6.8364-11.7593,11.7456
c-2.8975,4.9248-4.3687,10.332-4.3721,16.0713c-0.0034,5.7188,1.4966,11.0654,4.4565,15.8887
c2.9893,4.9209,6.8789,8.7334,11.8887,11.6514c4.8657,2.8633,10.2397,4.3174,15.9717,4.3203c0.0073,0,0.0146,0,0.022,0
c8.123,0,14.7441-2.5869,21.4683-8.3906c0.5493-0.4805,0.8516-1.1201,0.8516-1.8008c0.001-0.6074-0.1743-1.1035-0.5205-1.4756
l-1.3569-1.8428l-0.0732-0.0859c-0.2637-0.2637-0.6929-0.6152-1.3716-0.6152c-0.6421,0-1.2549,0.2217-1.7124,0.6143
c-1.9346,1.585-3.5459,2.8008-4.7969,3.6182c-1.7979,1.208-5.8218,3.2314-12.5986,3.2314c-0.0073,0-0.0142,0-0.021,0
c-0.1357,0.0029-0.269,0.0039-0.4043,0.0039c-12.2642,0-23.4736-10.3262-24.5088-22.4814l53.0127,0.0322c0.0015,0,0.0024,0,0.0034,0
c2.2373,0,3.4697-1.1621,3.4712-3.2715c0.0034-5.2588-1.3574-10.3945-4.0464-15.2705
C155.0015,84.0953,151.2188,80.1363,146.4561,77.1739z M154.6451,100.4341l-49.1157-0.0293
c1.9834-11.6631,13.0181-21.2417,24.5034-21.0117c0.1387-0.0024,0.2754-0.0039,0.4136-0.0039
C143.4629,79.3892,152.4722,90.0162,154.6451,100.4341z"/>
<path style="fill:#181139;" d="M204.0386,136.6382c5.7319,0,11.1069-1.4502,15.9746-4.3115
c4.938-2.9014,8.75-6.7129,11.6533-11.6533c2.8608-4.8672,4.311-10.2578,4.311-16.0244c0-5.7324-1.4502-11.1064-4.311-15.9746
c-2.9019-4.9385-6.7134-8.75-11.6533-11.6533c-4.8687-2.8618-10.2437-4.3125-15.9746-4.3125
c-9.938,0-19.2021,4.7583-24.3516,12.3174v-9.438c0-0.5946-0.1465-1.0788-0.411-1.4511c-0.3815-0.5369-1.0157-0.834-1.8727-0.834
h-2.6738c-1.4521,0-2.2852,0.833-2.2852,2.2852v5.6964v46.4791v23.9676c0,1.2568,0.7808,2.0371,2.0371,2.0371h3.3667
c0.9209,0,1.6421-0.6992,1.6421-1.5908v-17.098v-10.984C185.0884,131.8892,194.2749,136.6382,204.0386,136.6382z M186.6358,122.5591
c-4.9346-4.9346-7.6831-11.4932-7.542-18.0254c-0.1367-6.3506,2.5439-12.751,7.3545-17.5605
c4.8521-4.8521,11.3037-7.5547,17.7383-7.417c4.3691,0,8.4863,1.1465,12.2314,3.4043c3.7344,2.2979,6.7456,5.4053,8.9492,9.2354
c2.1699,3.9072,3.2695,8.0967,3.2695,12.4697c0.1396,6.4619-2.5967,12.9844-7.5083,17.8955
c-4.7617,4.7617-11.0469,7.3857-17.2544,7.2803C197.6856,129.9712,191.396,127.3208,186.6358,122.5591z"/>
<path style="fill:#181139;" d="M241.8955,80.3975h7.5669v42.0259c0,6.8174,4.5674,12.1309,11.0825,12.9189
c0.6836,0.1055,1.8379,0.1572,3.5303,0.1572c2.0078,0,3.0273-0.3535,3.0273-2.2842v-2.377c0-1.7891-1.334-2.0371-2.7568-2.0371
c0,0-0.001,0-0.002,0l-1.7871-0.0488c-2.0117-0.0439-3.4883-0.7627-4.3896-2.1367c-0.9697-1.4805-1.4619-3.1738-1.4619-5.0352
V80.3975h10.0928c1.3076,0,2.2852-1.3628,2.2852-2.5815v-1.9312c0-1.3999-0.8359-2.2354-2.2354-2.2354h-10.1426V60.6861
c0-1.4619-0.7969-2.4829-1.9375-2.4829c-0.1865,0-0.4121,0-0.6392,0.0884l-2.6489,0.6865
c-1.2109,0.3682-2.0171,0.9263-2.0171,2.4507v12.2207h-7.5669c-1.4185,0-2.335,0.897-2.335,2.2852v1.8813
C239.5606,79.2393,240.6079,80.3975,241.8955,80.3975z"/>
<path style="fill:#181139;" d="M379.1182,106.2691c-4.0488-2.9219-8.8545-5.0293-14.291-6.2646
c-6.5049-1.3975-13.4473-5.2129-13.3203-10.3066c0-7.5225,6.6367-10.1914,12.3203-10.1914c5.3574,0,10.2207,3.002,13.001,8.0146
c0.6729,1.2861,1.4785,1.9375,2.3955,1.9375c0.3311,0,0.7061-0.1113,0.9922-0.2832l2.2021-1.1523
c0.5947-0.3408,0.9229-0.9414,0.9229-1.6924c0-0.5205-0.0908-0.9541-0.2617-1.292c-3.6367-8.2466-10.0967-12.4282-19.2021-12.4282
c-11.7305,0-19.6123,6.9263-19.6123,17.2349c0,4.3125,1.8438,7.9746,5.4756,10.8809c3.4482,2.7979,7.9121,4.8623,13.2705,6.1377
c4.5859,1.085,8.3193,2.5654,11.0977,4.4023c1.4159,0.9354,2.4412,2.0535,3.106,3.3672c0.6053,1.1962,0.9135,2.5535,0.9135,4.1005
c0.0742,2.3857-0.79,4.5176-2.5684,6.3389c-3.1445,3.2178-8.4053,4.6689-12.0205,4.6689c-0.0361,0-0.0723,0-0.1074,0
c-3.4268,0-6.4893-0.8438-9.1035-2.5068c-2.5918-1.6484-4.2363-3.8076-5.0293-6.6064c-0.3203-1.0996-0.751-2.1738-2.1553-2.1738
c-0.0742,0-0.2109,0.0146-0.4062,0.0449c-0.1133,0.0166-0.2559,0.0381-0.5088,0.0742l-1.8818,0.4463l-0.1045,0.0332
c-1.0244,0.4082-1.6113,1.1846-1.6113,2.1309c0,0.2285,0.0625,0.6592,0.2178,1.1094c1.9707,8.5801,10.2432,14.3447,20.5732,14.3447
c0.125,0.002,0.249,0.002,0.374,0.002c6.5947,0,12.6748-2.3193,16.7275-6.3945c3.1895-3.208,4.8311-7.2363,4.748-11.6357
c0-2.8187-0.6185-5.3109-1.8062-7.481C382.4437,109.2624,381.0062,107.631,379.1182,106.2691z"/>
<path style="fill:#EF3939;" d="M348.9043,45.7325c0-6.3157-3.2826-11.8699-8.2238-15.0756
c-2.811-1.8237-6.1537-2.8947-9.7469-2.8947c-9.9092,0-17.9707,8.0615-17.9707,17.9702c0,4.7659,1.8775,9.0925,4.9157,12.3123
c-3.6619,4.3709-6.6334,9.3336-8.7663,14.7186c-1.5873-0.2422-3.2123-0.3683-4.8662-0.3683
c-17.7158,0-32.1289,14.4131-32.1289,32.1289c0,14.6854,9.9077,27.0922,23.3869,30.9101
c-6.7762,17.3461-23.6572,29.6719-43.3742,29.6719c-16.8195,0-31.583-8.9662-39.7656-22.369
c-2.4778,0.5446-5.0429,0.8519-7.6721,0.9023c9.0226,16.99,26.8969,28.5917,47.4377,28.5917
c23.2646,0,43.1121-14.8788,50.5461-35.6179c0.5204,0.0251,1.0435,0.0398,1.5701,0.0398c17.7158,0,32.1289-14.4131,32.1289-32.1289
c0-13.557-8.4446-25.1712-20.3465-29.8811c1.9001-4.5678,4.5115-8.7646,7.6888-12.4641c0.9996,0.4404,2.0479,0.785,3.1324,1.0384
c1.3144,0.3071,2.6773,0.486,4.0839,0.486C340.8428,63.7032,348.9043,55.6416,348.9043,45.7325z M304.2461,129.5279
c-13.7871,0-25.0039-11.2168-25.0039-25.0039s11.2168-25.0039,25.0039-25.0039S329.25,90.7369,329.25,104.524
S318.0332,129.5279,304.2461,129.5279z M330.9336,34.8872c0.645,0,1.2737,0.0671,1.8881,0.1755
c5.0818,0.8974,8.9576,5.3347,8.9576,10.6697c0,5.9805-4.8652,10.8457-10.8457,10.8457s-10.8457-4.8652-10.8457-10.8457
c0-1.3967,0.2746-2.7282,0.7576-3.9555C322.4306,37.7496,326.35,34.8872,330.9336,34.8872z"/>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 437.4294 209.6185" style="enable-background:new 0 0 437.4294 209.6185;" xml:space="preserve">
<path d="M95.1109,128.1089H58.6797V65.6861c0-1.5234-0.8169-2.4331-2.1855-2.4331h-3.1187c-1.3159,0-2.2349,1.0005-2.2349,2.4331
v67.4297c0,1.4521,0.8145,2.2852,2.2349,2.2852h41.7353c1.4844,0,2.4819-0.9375,2.4819-2.333v-2.7744
C97.5928,128.9253,96.6651,128.1089,95.1109,128.1089z"/>
<path d="M146.4561,77.1739c-4.8252-3.001-10.3037-4.5249-16.2837-4.5288c-0.0068,0-0.0137,0-0.0205,0
c-5.7349,0-11.1377,1.4639-16.0566,4.3511c-4.916,2.8853-8.8721,6.8364-11.7593,11.7456
c-2.8975,4.9248-4.3687,10.332-4.3721,16.0713c-0.0034,5.7188,1.4966,11.0654,4.4565,15.8887
c2.9893,4.9209,6.8789,8.7334,11.8887,11.6514c4.8657,2.8633,10.2397,4.3174,15.9717,4.3203c0.0073,0,0.0146,0,0.022,0
c8.123,0,14.7441-2.5869,21.4683-8.3906c0.5493-0.4805,0.8516-1.1201,0.8516-1.8008c0.001-0.6074-0.1743-1.1035-0.5205-1.4756
l-1.3569-1.8428l-0.0732-0.0859c-0.2637-0.2637-0.6929-0.6152-1.3716-0.6152c-0.6421,0-1.2549,0.2217-1.7124,0.6143
c-1.9346,1.585-3.5459,2.8008-4.7969,3.6182c-1.7979,1.208-5.8218,3.2314-12.5986,3.2314c-0.0073,0-0.0142,0-0.021,0
c-0.1357,0.0029-0.269,0.0039-0.4043,0.0039c-12.2642,0-23.4736-10.3262-24.5088-22.4814l53.0127,0.0322c0.0015,0,0.0024,0,0.0034,0
c2.2373,0,3.4697-1.1621,3.4712-3.2715c0.0034-5.2588-1.3574-10.3945-4.0464-15.2705
C155.0015,84.0953,151.2188,80.1363,146.4561,77.1739z M154.6451,100.4341l-49.1157-0.0293
c1.9834-11.6631,13.0181-21.2417,24.5034-21.0117c0.1387-0.0024,0.2754-0.0039,0.4136-0.0039
C143.4629,79.3892,152.4722,90.0162,154.6451,100.4341z"/>
<path d="M204.0386,136.6382c5.7319,0,11.1069-1.4502,15.9746-4.3115c4.938-2.9014,8.75-6.7129,11.6533-11.6533
c2.8608-4.8672,4.311-10.2578,4.311-16.0244c0-5.7324-1.4502-11.1064-4.311-15.9746c-2.9019-4.9385-6.7134-8.75-11.6533-11.6533
c-4.8687-2.8618-10.2437-4.3125-15.9746-4.3125c-9.938,0-19.2021,4.7583-24.3516,12.3174v-9.438
c0-0.5946-0.1465-1.0788-0.411-1.4511c-0.3815-0.5369-1.0157-0.834-1.8727-0.834h-2.6738c-1.4521,0-2.2852,0.833-2.2852,2.2852
v5.6964v46.4791v23.9676c0,1.2568,0.7808,2.0371,2.0371,2.0371h3.3667c0.9209,0,1.6421-0.6992,1.6421-1.5908v-17.098v-10.984
C185.0884,131.8892,194.2749,136.6382,204.0386,136.6382z M186.6358,122.5591c-4.9346-4.9346-7.6831-11.4932-7.542-18.0254
c-0.1367-6.3506,2.5439-12.751,7.3545-17.5605c4.8521-4.8521,11.3037-7.5547,17.7383-7.417c4.3691,0,8.4863,1.1465,12.2314,3.4043
c3.7344,2.2979,6.7456,5.4053,8.9492,9.2354c2.1699,3.9072,3.2695,8.0967,3.2695,12.4697
c0.1396,6.4619-2.5967,12.9844-7.5083,17.8955c-4.7617,4.7617-11.0469,7.3857-17.2544,7.2803
C197.6856,129.9712,191.396,127.3208,186.6358,122.5591z"/>
<path d="M241.8955,80.3975h7.5669v42.0259c0,6.8174,4.5674,12.1309,11.0825,12.9189c0.6836,0.1055,1.8379,0.1572,3.5303,0.1572
c2.0078,0,3.0273-0.3535,3.0273-2.2842v-2.377c0-1.7891-1.334-2.0371-2.7568-2.0371c0,0-0.001,0-0.002,0l-1.7871-0.0488
c-2.0117-0.0439-3.4883-0.7627-4.3896-2.1367c-0.9697-1.4805-1.4619-3.1738-1.4619-5.0352V80.3975h10.0928
c1.3076,0,2.2852-1.3628,2.2852-2.5815v-1.9312c0-1.3999-0.8359-2.2354-2.2354-2.2354h-10.1426V60.6861
c0-1.4619-0.7969-2.4829-1.9375-2.4829c-0.1865,0-0.4121,0-0.6392,0.0884l-2.6489,0.6865
c-1.2109,0.3682-2.0171,0.9263-2.0171,2.4507v12.2207h-7.5669c-1.4185,0-2.335,0.897-2.335,2.2852v1.8813
C239.5606,79.2393,240.6079,80.3975,241.8955,80.3975z"/>
<path d="M379.1182,106.2691c-4.0488-2.9219-8.8545-5.0293-14.291-6.2646c-6.5049-1.3975-13.4473-5.2129-13.3203-10.3066
c0-7.5225,6.6367-10.1914,12.3203-10.1914c5.3574,0,10.2207,3.002,13.001,8.0146c0.6729,1.2861,1.4785,1.9375,2.3955,1.9375
c0.3311,0,0.7061-0.1113,0.9922-0.2832l2.2021-1.1523c0.5947-0.3408,0.9229-0.9414,0.9229-1.6924
c0-0.5205-0.0908-0.9541-0.2617-1.292c-3.6367-8.2466-10.0967-12.4282-19.2021-12.4282c-11.7305,0-19.6123,6.9263-19.6123,17.2349
c0,4.3125,1.8438,7.9746,5.4756,10.8809c3.4482,2.7979,7.9121,4.8623,13.2705,6.1377c4.5859,1.085,8.3193,2.5654,11.0977,4.4023
c1.4159,0.9354,2.4412,2.0535,3.106,3.3672c0.6053,1.1962,0.9135,2.5535,0.9135,4.1005c0.0742,2.3857-0.79,4.5176-2.5684,6.3389
c-3.1445,3.2178-8.4053,4.6689-12.0205,4.6689c-0.0361,0-0.0723,0-0.1074,0c-3.4268,0-6.4893-0.8438-9.1035-2.5068
c-2.5918-1.6484-4.2363-3.8076-5.0293-6.6064c-0.3203-1.0996-0.751-2.1738-2.1553-2.1738c-0.0742,0-0.2109,0.0146-0.4062,0.0449
c-0.1133,0.0166-0.2559,0.0381-0.5088,0.0742l-1.8818,0.4463l-0.1045,0.0332c-1.0244,0.4082-1.6113,1.1846-1.6113,2.1309
c0,0.2285,0.0625,0.6592,0.2178,1.1094c1.9707,8.5801,10.2432,14.3447,20.5732,14.3447c0.125,0.002,0.249,0.002,0.374,0.002
c6.5947,0,12.6748-2.3193,16.7275-6.3945c3.1895-3.208,4.8311-7.2363,4.748-11.6357c0-2.8187-0.6185-5.3109-1.8062-7.481
C382.4437,109.2624,381.0062,107.631,379.1182,106.2691z"/>
<path d="M348.9043,45.7325c0-6.3157-3.2826-11.8699-8.2238-15.0756c-2.811-1.8237-6.1537-2.8947-9.7469-2.8947
c-9.9092,0-17.9707,8.0615-17.9707,17.9702c0,4.7659,1.8775,9.0925,4.9157,12.3123c-3.6619,4.3709-6.6334,9.3336-8.7663,14.7186
c-1.5873-0.2422-3.2123-0.3683-4.8662-0.3683c-17.7158,0-32.1289,14.4131-32.1289,32.1289c0,14.6854,9.9077,27.0922,23.3869,30.9101
c-6.7762,17.3461-23.6572,29.6719-43.3742,29.6719c-16.8195,0-31.583-8.9662-39.7656-22.369
c-2.4778,0.5446-5.0429,0.8519-7.6721,0.9023c9.0226,16.99,26.8969,28.5917,47.4377,28.5917
c23.2646,0,43.1121-14.8788,50.5461-35.6179c0.5204,0.0251,1.0435,0.0398,1.5701,0.0398c17.7158,0,32.1289-14.4131,32.1289-32.1289
c0-13.557-8.4446-25.1712-20.3465-29.8811c1.9001-4.5678,4.5115-8.7646,7.6888-12.4641c0.9996,0.4404,2.0479,0.785,3.1324,1.0384
c1.3144,0.3071,2.6773,0.486,4.0839,0.486C340.8428,63.7032,348.9043,55.6416,348.9043,45.7325z M304.2461,129.5279
c-13.7871,0-25.0039-11.2168-25.0039-25.0039s11.2168-25.0039,25.0039-25.0039S329.25,90.7369,329.25,104.524
S318.0332,129.5279,304.2461,129.5279z M330.9336,34.8872c0.645,0,1.2737,0.0671,1.8881,0.1755
c5.0818,0.8974,8.9576,5.3347,8.9576,10.6697c0,5.9805-4.8652,10.8457-10.8457,10.8457s-10.8457-4.8652-10.8457-10.8457
c0-1.3967,0.2746-2.7282,0.7576-3.9555C322.4306,37.7496,326.35,34.8872,330.9336,34.8872z"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 437.4294 209.6185" style="enable-background:new 0 0 437.4294 209.6185;" xml:space="preserve">
<path style="fill:#FFFFFF;" d="M95.1109,128.1089H58.6797V65.6861c0-1.5234-0.8169-2.4331-2.1855-2.4331h-3.1187
c-1.3159,0-2.2349,1.0005-2.2349,2.4331v67.4297c0,1.4521,0.8145,2.2852,2.2349,2.2852h41.7353c1.4844,0,2.4819-0.9375,2.4819-2.333
v-2.7744C97.5928,128.9253,96.6651,128.1089,95.1109,128.1089z"/>
<path style="fill:#FFFFFF;" d="M146.4561,77.1739c-4.8252-3.001-10.3037-4.5249-16.2837-4.5288c-0.0068,0-0.0137,0-0.0205,0
c-5.7349,0-11.1377,1.4639-16.0566,4.3511c-4.916,2.8853-8.8721,6.8364-11.7593,11.7456
c-2.8975,4.9248-4.3687,10.332-4.3721,16.0713c-0.0034,5.7188,1.4966,11.0654,4.4565,15.8887
c2.9893,4.9209,6.8789,8.7334,11.8887,11.6514c4.8657,2.8633,10.2397,4.3174,15.9717,4.3203c0.0073,0,0.0146,0,0.022,0
c8.123,0,14.7441-2.5869,21.4683-8.3906c0.5493-0.4805,0.8516-1.1201,0.8516-1.8008c0.001-0.6074-0.1743-1.1035-0.5205-1.4756
l-1.3569-1.8428l-0.0732-0.0859c-0.2637-0.2637-0.6929-0.6152-1.3716-0.6152c-0.6421,0-1.2549,0.2217-1.7124,0.6143
c-1.9346,1.585-3.5459,2.8008-4.7969,3.6182c-1.7979,1.208-5.8218,3.2314-12.5986,3.2314c-0.0073,0-0.0142,0-0.021,0
c-0.1357,0.0029-0.269,0.0039-0.4043,0.0039c-12.2642,0-23.4736-10.3262-24.5088-22.4814l53.0127,0.0322c0.0015,0,0.0024,0,0.0034,0
c2.2373,0,3.4697-1.1621,3.4712-3.2715c0.0034-5.2588-1.3574-10.3945-4.0464-15.2705
C155.0015,84.0953,151.2188,80.1363,146.4561,77.1739z M154.6451,100.4341l-49.1157-0.0293
c1.9834-11.6631,13.0181-21.2417,24.5034-21.0117c0.1387-0.0024,0.2754-0.0039,0.4136-0.0039
C143.4629,79.3892,152.4722,90.0162,154.6451,100.4341z"/>
<path style="fill:#FFFFFF;" d="M204.0386,136.6382c5.7319,0,11.1069-1.4502,15.9746-4.3115
c4.938-2.9014,8.75-6.7129,11.6533-11.6533c2.8608-4.8672,4.311-10.2578,4.311-16.0244c0-5.7324-1.4502-11.1064-4.311-15.9746
c-2.9019-4.9385-6.7134-8.75-11.6533-11.6533c-4.8687-2.8618-10.2437-4.3125-15.9746-4.3125
c-9.938,0-19.2021,4.7583-24.3516,12.3174v-9.438c0-0.5946-0.1465-1.0788-0.411-1.4511c-0.3815-0.5369-1.0157-0.834-1.8727-0.834
h-2.6738c-1.4521,0-2.2852,0.833-2.2852,2.2852v5.6964v46.4791v23.9676c0,1.2568,0.7808,2.0371,2.0371,2.0371h3.3667
c0.9209,0,1.6421-0.6992,1.6421-1.5908v-17.098v-10.984C185.0884,131.8892,194.2749,136.6382,204.0386,136.6382z M186.6358,122.5591
c-4.9346-4.9346-7.6831-11.4932-7.542-18.0254c-0.1367-6.3506,2.5439-12.751,7.3545-17.5605
c4.8521-4.8521,11.3037-7.5547,17.7383-7.417c4.3691,0,8.4863,1.1465,12.2314,3.4043c3.7344,2.2979,6.7456,5.4053,8.9492,9.2354
c2.1699,3.9072,3.2695,8.0967,3.2695,12.4697c0.1396,6.4619-2.5967,12.9844-7.5083,17.8955
c-4.7617,4.7617-11.0469,7.3857-17.2544,7.2803C197.6856,129.9712,191.396,127.3208,186.6358,122.5591z"/>
<path style="fill:#FFFFFF;" d="M241.8955,80.3975h7.5669v42.0259c0,6.8174,4.5674,12.1309,11.0825,12.9189
c0.6836,0.1055,1.8379,0.1572,3.5303,0.1572c2.0078,0,3.0273-0.3535,3.0273-2.2842v-2.377c0-1.7891-1.334-2.0371-2.7568-2.0371
c0,0-0.001,0-0.002,0l-1.7871-0.0488c-2.0117-0.0439-3.4883-0.7627-4.3896-2.1367c-0.9697-1.4805-1.4619-3.1738-1.4619-5.0352
V80.3975h10.0928c1.3076,0,2.2852-1.3628,2.2852-2.5815v-1.9312c0-1.3999-0.8359-2.2354-2.2354-2.2354h-10.1426V60.6861
c0-1.4619-0.7969-2.4829-1.9375-2.4829c-0.1865,0-0.4121,0-0.6392,0.0884l-2.6489,0.6865
c-1.2109,0.3682-2.0171,0.9263-2.0171,2.4507v12.2207h-7.5669c-1.4185,0-2.335,0.897-2.335,2.2852v1.8813
C239.5606,79.2393,240.6079,80.3975,241.8955,80.3975z"/>
<path style="fill:#FFFFFF;" d="M379.1182,106.2691c-4.0488-2.9219-8.8545-5.0293-14.291-6.2646
c-6.5049-1.3975-13.4473-5.2129-13.3203-10.3066c0-7.5225,6.6367-10.1914,12.3203-10.1914c5.3574,0,10.2207,3.002,13.001,8.0146
c0.6729,1.2861,1.4785,1.9375,2.3955,1.9375c0.3311,0,0.7061-0.1113,0.9922-0.2832l2.2021-1.1523
c0.5947-0.3408,0.9229-0.9414,0.9229-1.6924c0-0.5205-0.0908-0.9541-0.2617-1.292c-3.6367-8.2466-10.0967-12.4282-19.2021-12.4282
c-11.7305,0-19.6123,6.9263-19.6123,17.2349c0,4.3125,1.8438,7.9746,5.4756,10.8809c3.4482,2.7979,7.9121,4.8623,13.2705,6.1377
c4.5859,1.085,8.3193,2.5654,11.0977,4.4023c1.4159,0.9354,2.4412,2.0535,3.106,3.3672c0.6053,1.1962,0.9135,2.5535,0.9135,4.1005
c0.0742,2.3857-0.79,4.5176-2.5684,6.3389c-3.1445,3.2178-8.4053,4.6689-12.0205,4.6689c-0.0361,0-0.0723,0-0.1074,0
c-3.4268,0-6.4893-0.8438-9.1035-2.5068c-2.5918-1.6484-4.2363-3.8076-5.0293-6.6064c-0.3203-1.0996-0.751-2.1738-2.1553-2.1738
c-0.0742,0-0.2109,0.0146-0.4062,0.0449c-0.1133,0.0166-0.2559,0.0381-0.5088,0.0742l-1.8818,0.4463l-0.1045,0.0332
c-1.0244,0.4082-1.6113,1.1846-1.6113,2.1309c0,0.2285,0.0625,0.6592,0.2178,1.1094c1.9707,8.5801,10.2432,14.3447,20.5732,14.3447
c0.125,0.002,0.249,0.002,0.374,0.002c6.5947,0,12.6748-2.3193,16.7275-6.3945c3.1895-3.208,4.8311-7.2363,4.748-11.6357
c0-2.8187-0.6185-5.3109-1.8062-7.481C382.4437,109.2624,381.0062,107.631,379.1182,106.2691z"/>
<path style="fill:#FFFFFF;" d="M348.9043,45.7325c0-6.3157-3.2826-11.8699-8.2238-15.0756
c-2.811-1.8237-6.1537-2.8947-9.7469-2.8947c-9.9092,0-17.9707,8.0615-17.9707,17.9702c0,4.7659,1.8775,9.0925,4.9157,12.3123
c-3.6619,4.3709-6.6334,9.3336-8.7663,14.7186c-1.5873-0.2422-3.2123-0.3683-4.8662-0.3683
c-17.7158,0-32.1289,14.4131-32.1289,32.1289c0,14.6854,9.9077,27.0922,23.3869,30.9101
c-6.7762,17.3461-23.6572,29.6719-43.3742,29.6719c-16.8195,0-31.583-8.9662-39.7656-22.369
c-2.4778,0.5446-5.0429,0.8519-7.6721,0.9023c9.0226,16.99,26.8969,28.5917,47.4377,28.5917
c23.2646,0,43.1121-14.8788,50.5461-35.6179c0.5204,0.0251,1.0435,0.0398,1.5701,0.0398c17.7158,0,32.1289-14.4131,32.1289-32.1289
c0-13.557-8.4446-25.1712-20.3465-29.8811c1.9001-4.5678,4.5115-8.7646,7.6888-12.4641c0.9996,0.4404,2.0479,0.785,3.1324,1.0384
c1.3144,0.3071,2.6773,0.486,4.0839,0.486C340.8428,63.7032,348.9043,55.6416,348.9043,45.7325z M304.2461,129.5279
c-13.7871,0-25.0039-11.2168-25.0039-25.0039s11.2168-25.0039,25.0039-25.0039S329.25,90.7369,329.25,104.524
S318.0332,129.5279,304.2461,129.5279z M330.9336,34.8872c0.645,0,1.2737,0.0671,1.8881,0.1755
c5.0818,0.8974,8.9576,5.3347,8.9576,10.6697c0,5.9805-4.8652,10.8457-10.8457,10.8457s-10.8457-4.8652-10.8457-10.8457
c0-1.3967,0.2746-2.7282,0.7576-3.9555C322.4306,37.7496,326.35,34.8872,330.9336,34.8872z"/>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 115.9988 115.9988" style="enable-background:new 0 0 115.9988 115.9988;" xml:space="preserve">
<g>
<g>
<g>
<path style="fill:#180D38;" d="M29.1281,108.2941c9.5736-4.5548,17.1531-12.6456,21.0335-22.5787
c-12.0865-3.4232-20.9707-14.548-20.9707-27.7159c0-15.8849,12.9236-28.8085,28.8085-28.8085
c1.4832,0,2.9404,0.113,4.3639,0.3303c1.9125-4.8287,4.5771-9.2786,7.8607-13.1979c-2.7243-2.8871-4.4077-6.7665-4.4077-11.0399
c0-1.6191,0.2457-3.1808,0.6921-4.6562C63.7305,0.2186,60.8908,0,57.9995,0C25.9672,0,0,25.9672,0,57.9994
C0,79.5165,11.7263,98.2828,29.1281,108.2941z"/>
<path style="fill:#EF3939;" d="M81.9297,15.0082c3.6788,0,6.886-2.0536,8.5379-5.0742
c-5.3185-3.5997-11.2684-6.3339-17.646-8.0151c-0.3903,1.0504-0.6168,2.1798-0.6168,3.3644
C72.2049,10.6458,76.5673,15.0082,81.9297,15.0082z"/>
<path style="fill:#180D38;" d="M95.5663,13.828c-2.8537,4.5375-7.8918,7.5688-13.6366,7.5688
c-1.2614,0-2.4835-0.1604-3.6622-0.4359c-0.9722-0.2272-1.9121-0.5362-2.8083-0.931c-2.8492,3.3173-5.1907,7.0806-6.8945,11.1766
c10.6715,4.2233,18.2432,14.6371,18.2432,26.7928c0,15.8849-12.9235,28.8085-28.8085,28.8085
c-0.4718,0-0.9406-0.0131-1.4069-0.0357c-3.7532,10.4704-11.0354,19.2744-20.406,24.9696
c6.7355,2.7367,14.0948,4.257,21.8129,4.257c32.0322,0,57.9994-25.9672,57.9994-57.9995
C115.9988,40.3018,108.0628,24.4664,95.5663,13.828z"/>
<circle style="fill:#EF3939;" cx="57.9994" cy="57.9994" r="22.4198"/>
</g>
<path style="fill:#FFFFFF;" d="M78.2676,20.961c1.1786,0.2755,2.4008,0.4359,3.6622,0.4359
c5.7448,0,10.7829-3.0313,13.6366-7.5688c-1.6275-1.3855-3.3236-2.6925-5.0987-3.894c-1.6519,3.0206-4.8591,5.0742-8.5379,5.0742
c-5.3624,0-9.7249-4.3624-9.7249-9.7249c0-1.1846,0.2264-2.3141,0.6168-3.3644c-2.062-0.5436-4.1682-0.9763-6.3133-1.2917
c-0.4464,1.4753-0.6921,3.0371-0.6921,4.6562c0,4.2734,1.6834,8.1528,4.4077,11.0399c-3.2836,3.9193-5.9482,8.3692-7.8607,13.1979
c-1.4235-0.2172-2.8807-0.3303-4.3639-0.3303c-15.8849,0-28.8085,12.9235-28.8085,28.8085
c0,13.168,8.8842,24.2928,20.9707,27.7159c-3.8804,9.9332-11.4599,18.0239-21.0335,22.5787
c2.2621,1.3013,4.6175,2.456,7.0584,3.4478c9.3706-5.6952,16.6528-14.4992,20.406-24.9696
c0.4663,0.0226,0.9351,0.0357,1.4069,0.0357c15.8849,0,28.8085-12.9236,28.8085-28.8085c0-12.1557-7.5717-22.5695-18.2432-26.7928
c1.7038-4.0959,4.0453-7.8593,6.8945-11.1766C76.3555,20.4248,77.2953,20.7338,78.2676,20.961z M80.4193,57.9994
c0,12.3623-10.0576,22.4199-22.4198,22.4199c-12.3623,0-22.4199-10.0576-22.4199-22.4199
c0-12.3622,10.0576-22.4199,22.4199-22.4199C70.3617,35.5795,80.4193,45.6371,80.4193,57.9994z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 115.5026 115.5026" style="enable-background:new 0 0 115.5026 115.5026;" xml:space="preserve">
<path style="fill:#181139;" d="M115.5026,0h-13.957c0.0002,0.0315,0.0031,0.0623,0.0031,0.0938
c0,9.718-7.9059,17.6239-17.6239,17.6239c-1.3796,0-2.7163-0.1754-4.0055-0.4767c-1.0634-0.2485-2.0913-0.5864-3.0715-1.0182
c-3.1162,3.6283-5.6772,7.7443-7.5408,12.2242c11.6719,4.6192,19.9532,16.0091,19.9532,29.3043
c0,17.374-14.1349,31.5089-31.5089,31.5089c-0.5161,0-1.0288-0.0143-1.5388-0.039c-3.8856,10.8397-11.2302,20.0454-20.6959,26.2814
h79.986V0z"/>
<circle style="fill:#EF3939;" cx="57.7513" cy="57.7513" r="24.5214"/>
<path style="fill:#181139;" d="M49.1788,88.0652c-13.2195-3.744-22.9364-15.9116-22.9364-30.3139
c0-17.374,14.1349-31.5089,31.5089-31.5089c1.6223,0,3.2161,0.1237,4.7729,0.3612c2.0918-5.2813,5.0061-10.1484,8.5975-14.4351
c-2.9796-3.1577-4.8209-7.4008-4.8209-12.0747c0-0.0317,0.0046-0.0622,0.0048-0.0938H0v115.5026h18.8623
C32.7495,111.6378,43.9877,101.3537,49.1788,88.0652z"/>
<path style="fill:#EF3939;" d="M83.9248,10.7302c5.8651,0,10.6364-4.7714,10.6364-10.6364c0-0.0316-0.004-0.0623-0.0043-0.0938
H73.293c-0.0003,0.0316-0.0046,0.0621-0.0046,0.0938C73.2884,5.9589,78.0598,10.7302,83.9248,10.7302z"/>
<path style="fill:#FFFFFF;" d="M56.2125,89.2212c0.51,0.0247,1.0228,0.039,1.5388,0.039c17.374,0,31.5089-14.1349,31.5089-31.5089
c0-13.2952-8.2814-24.6851-19.9532-29.3043c1.8635-4.4799,4.4246-8.5959,7.5408-12.2242c0.9802,0.4318,2.0082,0.7698,3.0715,1.0182
c1.2892,0.3013,2.6259,0.4767,4.0055,0.4767c9.718,0,17.6239-7.9059,17.6239-17.6239c0-0.0315-0.0029-0.0623-0.0031-0.0938h-6.9887
c0.0003,0.0316,0.0043,0.0622,0.0043,0.0938c0,5.8651-4.7714,10.6364-10.6364,10.6364S73.2884,5.9589,73.2884,0.0938
c0-0.0317,0.0043-0.0622,0.0046-0.0938h-6.9874c-0.0002,0.0316-0.0048,0.0621-0.0048,0.0938c0,4.674,1.8413,8.9171,4.8209,12.0747
c-3.5914,4.2867-6.5057,9.1537-8.5975,14.4351c-1.5569-0.2375-3.1507-0.3612-4.7729-0.3612
c-17.374,0-31.5089,14.1349-31.5089,31.5089c0,14.4023,9.7169,26.5699,22.9364,30.3139
c-5.1912,13.2885-16.4293,23.5726-30.3165,27.4374h16.6543C44.9824,109.2666,52.327,100.0609,56.2125,89.2212z M33.2299,57.7513
c0-13.5211,11.0004-24.5214,24.5214-24.5214s24.5214,11.0004,24.5214,24.5214S71.2724,82.2727,57.7513,82.2727
S33.2299,71.2723,33.2299,57.7513z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 437.4 209.6" style="enable-background:new 0 0 437.4 209.6;" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3939;}
</style>
<path class="st0" d="M95.1,128.1H58.7V65.7c0-1.5-0.8-2.4-2.2-2.4h-3.1c-1.3,0-2.2,1-2.2,2.4v67.4c0,1.5,0.8,2.3,2.2,2.3h41.7 c1.5,0,2.5-0.9,2.5-2.3v-2.8C97.6,128.9,96.7,128.1,95.1,128.1z"/>
<path class="st0" d="M146.5,77.2c-4.8-3-10.3-4.5-16.3-4.5c0,0,0,0,0,0c-5.7,0-11.1,1.5-16.1,4.4c-4.9,2.9-8.9,6.8-11.8,11.7 c-2.9,4.9-4.4,10.3-4.4,16.1c0,5.7,1.5,11.1,4.5,15.9c3,4.9,6.9,8.7,11.9,11.7c4.9,2.9,10.2,4.3,16,4.3c0,0,0,0,0,0 c8.1,0,14.7-2.6,21.5-8.4c0.5-0.5,0.9-1.1,0.9-1.8c0-0.6-0.2-1.1-0.5-1.5l-1.4-1.8l-0.1-0.1c-0.3-0.3-0.7-0.6-1.4-0.6 c-0.6,0-1.3,0.2-1.7,0.6c-1.9,1.6-3.5,2.8-4.8,3.6c-1.8,1.2-5.8,3.2-12.6,3.2c0,0,0,0,0,0c-0.1,0-0.3,0-0.4,0 c-12.3,0-23.5-10.3-24.5-22.5l53,0c0,0,0,0,0,0c2.2,0,3.5-1.2,3.5-3.3c0-5.3-1.4-10.4-4-15.3C155,84.1,151.2,80.1,146.5,77.2z M154.6,100.4l-49.1,0c2-11.7,13-21.2,24.5-21c0.1,0,0.3,0,0.4,0C143.5,79.4,152.5,90,154.6,100.4z"/>
<path class="st0" d="M204,136.6c5.7,0,11.1-1.5,16-4.3c4.9-2.9,8.8-6.7,11.7-11.7c2.9-4.9,4.3-10.3,4.3-16c0-5.7-1.5-11.1-4.3-16 c-2.9-4.9-6.7-8.8-11.7-11.7c-4.9-2.9-10.2-4.3-16-4.3c-9.9,0-19.2,4.8-24.4,12.3v-9.4c0-0.6-0.1-1.1-0.4-1.5 c-0.4-0.5-1-0.8-1.9-0.8h-2.7c-1.5,0-2.3,0.8-2.3,2.3v5.7v46.5v24c0,1.3,0.8,2,2,2h3.4c0.9,0,1.6-0.7,1.6-1.6v-17.1v-11 C185.1,131.9,194.3,136.6,204,136.6z M186.6,122.6c-4.9-4.9-7.7-11.5-7.5-18c-0.1-6.4,2.5-12.8,7.4-17.6c4.9-4.9,11.3-7.6,17.7-7.4 c4.4,0,8.5,1.1,12.2,3.4c3.7,2.3,6.7,5.4,8.9,9.2c2.2,3.9,3.3,8.1,3.3,12.5c0.1,6.5-2.6,13-7.5,17.9c-4.8,4.8-11,7.4-17.3,7.3 C197.7,130,191.4,127.3,186.6,122.6z"/>
<path class="st0" d="M241.9,80.4h7.6v42c0,6.8,4.6,12.1,11.1,12.9c0.7,0.1,1.8,0.2,3.5,0.2c2,0,3-0.4,3-2.3v-2.4c0-1.8-1.3-2-2.8-2 c0,0,0,0,0,0l-1.8,0c-2,0-3.5-0.8-4.4-2.1c-1-1.5-1.5-3.2-1.5-5V80.4h10.1c1.3,0,2.3-1.4,2.3-2.6v-1.9c0-1.4-0.8-2.2-2.2-2.2h-10.1 v-13c0-1.5-0.8-2.5-1.9-2.5c-0.2,0-0.4,0-0.6,0.1l-2.6,0.7c-1.2,0.4-2,0.9-2,2.5v12.2h-7.6c-1.4,0-2.3,0.9-2.3,2.3v1.9 C239.6,79.2,240.6,80.4,241.9,80.4z"/>
<path class="st0" d="M379.1,106.3c-4-2.9-8.9-5-14.3-6.3c-6.5-1.4-13.4-5.2-13.3-10.3c0-7.5,6.6-10.2,12.3-10.2c5.4,0,10.2,3,13,8 c0.7,1.3,1.5,1.9,2.4,1.9c0.3,0,0.7-0.1,1-0.3l2.2-1.2c0.6-0.3,0.9-0.9,0.9-1.7c0-0.5-0.1-1-0.3-1.3c-3.6-8.2-10.1-12.4-19.2-12.4 c-11.7,0-19.6,6.9-19.6,17.2c0,4.3,1.8,8,5.5,10.9c3.4,2.8,7.9,4.9,13.3,6.1c4.6,1.1,8.3,2.6,11.1,4.4c1.4,0.9,2.4,2.1,3.1,3.4 c0.6,1.2,0.9,2.6,0.9,4.1c0.1,2.4-0.8,4.5-2.6,6.3c-3.1,3.2-8.4,4.7-12,4.7c0,0-0.1,0-0.1,0c-3.4,0-6.5-0.8-9.1-2.5 c-2.6-1.6-4.2-3.8-5-6.6c-0.3-1.1-0.8-2.2-2.2-2.2c-0.1,0-0.2,0-0.4,0c-0.1,0-0.3,0-0.5,0.1l-1.9,0.4l-0.1,0c-1,0.4-1.6,1.2-1.6,2.1 c0,0.2,0.1,0.7,0.2,1.1c2,8.6,10.2,14.3,20.6,14.3c0.1,0,0.2,0,0.4,0c6.6,0,12.7-2.3,16.7-6.4c3.2-3.2,4.8-7.2,4.7-11.6 c0-2.8-0.6-5.3-1.8-7.5C382.4,109.3,381,107.6,379.1,106.3z"/>
<path class="st1" d="M348.9,45.7c0-6.3-3.3-11.9-8.2-15.1c-2.8-1.8-6.2-2.9-9.7-2.9c-9.9,0-18,8.1-18,18c0,4.8,1.9,9.1,4.9,12.3 c-3.7,4.4-6.6,9.3-8.8,14.7c-1.6-0.2-3.2-0.4-4.9-0.4c-17.7,0-32.1,14.4-32.1,32.1c0,14.7,9.9,27.1,23.4,30.9 c-6.8,17.3-23.7,29.7-43.4,29.7c-16.8,0-31.6-9-39.8-22.4c-2.5,0.5-5,0.9-7.7,0.9c9,17,26.9,28.6,47.4,28.6 c23.3,0,43.1-14.9,50.5-35.6c0.5,0,1,0,1.6,0c17.7,0,32.1-14.4,32.1-32.1c0-13.6-8.4-25.2-20.3-29.9c1.9-4.6,4.5-8.8,7.7-12.5 c1,0.4,2,0.8,3.1,1c1.3,0.3,2.7,0.5,4.1,0.5C340.8,63.7,348.9,55.6,348.9,45.7z M304.2,129.5c-13.8,0-25-11.2-25-25s11.2-25,25-25 s25,11.2,25,25S318,129.5,304.2,129.5z M330.9,34.9c0.6,0,1.3,0.1,1.9,0.2c5.1,0.9,9,5.3,9,10.7c0,6-4.9,10.8-10.8,10.8 s-10.8-4.9-10.8-10.8c0-1.4,0.3-2.7,0.8-4C322.4,37.7,326.4,34.9,330.9,34.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -3,3 +3,5 @@
This example creates a simple counter in a client side rendered app with Rust and WASM!
To run it, just issue the `trunk serve --open` command in the example root. This will build the app, run it, and open a new browser to serve it.
> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/)

View File

@@ -2,6 +2,7 @@
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
</head>
<body></body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -17,7 +17,7 @@ pub fn SimpleCounter(
<div>
<button on:click=move |_| set_value(0)>"Clear"</button>
<button on:click=move |_| set_value.update(|value| *value -= step)>"-1"</button>
<span>"Value: " {move || value().to_string()} "!"</span>
<span>"Value: " {value} "!"</span>
<button on:click=move |_| set_value.update(|value| *value += step)>"+1"</button>
</div>
}

View File

@@ -23,7 +23,7 @@ leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_meta = { path = "../../meta", default-features = false }
leptos_router = { path = "../../router", default-features = false }
log = "0.4"
simple_logger = "2"
simple_logger = "4.0.0"
gloo-net = { git = "https://github.com/rustwasm/gloo" }
[features]
@@ -47,6 +47,7 @@ skip_feature_sets = [["ssr", "hydrate"]]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name = "counter_isomorphic"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
# When NOT using cargo-leptos this must be updated to "." or the counters will not work. The above warning still applies if you do switch to cargo-leptos later.
site-root = "target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
# Defaults to pkg
@@ -54,9 +55,9 @@ site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
# style-file = "src/styles/tailwind.css"
# [Optional] Files in the asset-dir will be copied to the site-root directory
# assets-dir = "static/assets"
assets-dir = "public"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-address = "127.0.0.1:3000"
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.

View File

@@ -3,8 +3,8 @@
This example demonstrates how to use a function isomorphically, to run a server side function from the browser and receive a result.
## Client Side Rendering
To run it as a Client Side App, you can issue `trunk serve --open` in the root. This will build the entire
app into one CSR bundle. Make sure you have trunk installed with `cargo install trunk`.
For this example the server must store the counter state since it can be modified by many users.
This means it is not possible to produce a working CSR-only version as a non-static server is required.
## Server Side Rendering with cargo-leptos
cargo-leptos is now the easiest and most featureful way to build server side rendered apps with hydration. It provides automatic recompilation of client and server code, wasm optimisation, CSS minification, and more! Check out more about it [here](https://github.com/akesson/cargo-leptos)
@@ -17,6 +17,9 @@ cargo install --locked cargo-leptos
```bash
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
3. When ready to deploy, run
```bash
cargo leptos build --release
@@ -25,7 +28,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. For examples with CSS you also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
1. Install wasm-pack
```bash
cargo install wasm-pack

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,5 +1,6 @@
use leptos::*;
use leptos_router::*;
use leptos_meta::*;
#[cfg(feature = "ssr")]
use std::sync::atomic::{AtomicI32, Ordering};
@@ -44,6 +45,7 @@ pub async fn clear_server_count() -> Result<i32, ServerFnError> {
}
#[component]
pub fn Counters(cx: Scope) -> impl IntoView {
provide_meta_context(cx);
view! {
cx,
<Router>
@@ -59,6 +61,7 @@ pub fn Counters(cx: Scope) -> impl IntoView {
<li><A href="multi">"Multi-User"</A></li>
</ul>
</nav>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<main>
<Routes>
<Route path="" view=|cx| view! {
@@ -113,7 +116,7 @@ pub fn Counter(cx: Scope) -> impl IntoView {
<div>
<button on:click=move |_| clear.dispatch(())>"Clear"</button>
<button on:click=move |_| dec.dispatch(())>"-1"</button>
<span>"Value: " {move || value().to_string()} "!"</span>
<span>"Value: " {value} "!"</span>
<button on:click=move |_| inc.dispatch(())>"+1"</button>
</div>
{move || error_msg().map(|msg| view! { cx, <p>"Error: " {msg.to_string()}</p>})}
@@ -209,8 +212,8 @@ pub fn MultiuserCounter(cx: Scope) -> impl IntoView {
};
#[cfg(feature = "ssr")]
let multiplayer_value =
create_signal_from_stream(cx, futures::stream::once(Box::pin(async { 0.to_string() })));
let (multiplayer_value, _) =
create_signal(cx, None::<i32>);
view! {
cx,

View File

@@ -9,6 +9,7 @@ cfg_if! {
use actix_files::{Files};
use actix_web::*;
use crate::counters::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
#[get("/api/events")]
async fn counter_events() -> impl Responder {
@@ -29,23 +30,26 @@ cfg_if! {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
crate::counters::register_server_functions();
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let pkg_dir = &leptos_options.site_pkg_dir;
let bundle_path = format!("/{site_root}/{pkg_dir}");
// Setting this to None means we'll be using cargo-leptos and its env vars.
// when not using cargo-leptos None must be replaced with Some("Cargo.toml")
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
let routes = generate_route_list(|cx| view! { cx, <Counters/> });
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
App::new()
.service(Files::new("/pkg", "./pkg")) // used by wasm-pack and cargo run. Can be removed if using cargo-leptos
.service(Files::new(&bundle_path, format!("./{bundle_path}"))) // used by cargo-leptos. Can be removed if using wasm-pack and cargo run.
.service(counter_events)
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
.route("/{tail:.*}", leptos_actix::render_app_to_stream(leptos_options.to_owned(), |cx| view! { cx, <Counters/> }))
//.wrap(middleware::Compress::default())
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), |cx| view! { cx, <Counters/> })
.service(Files::new("/", &site_root))
//.wrap(middleware::Compress::default())
})
.bind(&addr)?
.run()
@@ -53,14 +57,11 @@ cfg_if! {
}
}
// client-only stuff for Trunk
// client-only main for Trunk
else {
use counter_isomorphic::counters::*;
pub fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|cx| view! { cx, <Counter/> });
// isomorphic counters cannot work in a Client-Side-Rendered only
// app as a server is required to maintain state
}
}
}

View File

@@ -0,0 +1,13 @@
[package]
name = "counter_without_macros"
version = "0.1.0"
edition = "2021"
[dependencies]
leptos = { path = "../../leptos", features = ["stable"] }
console_log = "0.2"
log = "0.4"
console_error_panic_hook = "0.1.7"
[dev-dependencies]
wasm-bindgen-test = "0.3.0"

View File

@@ -0,0 +1,5 @@
# Leptos Counter Example
This example is the same like the `counter` but it's written without using macros and can be build with stable Rust.
To run it, just issue the `trunk serve --open` command in the example root. This will build the app, run it, and open a new browser to serve it.

View File

@@ -2,6 +2,7 @@
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
</head>
<body></body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,44 @@
use leptos::{ev, *};
pub struct Props {
/// The starting value for the counter
pub initial_value: i32,
/// The change that should be applied each time the button is clicked.
pub step: i32,
}
/// A simple counter view.
pub fn view(cx: Scope, props: Props) -> impl IntoView {
let Props {
initial_value,
step,
} = props;
let (value, set_value) = create_signal(cx, initial_value);
div(cx)
.child((
cx,
button(cx)
.on(ev::click, move |_| set_value.update(|value| *value = 0))
.child((cx, "Clear")),
))
.child((
cx,
button(cx)
.on(ev::click, move |_| set_value.update(|value| *value -= step))
.child((cx, "-1")),
))
.child((
cx,
span(cx)
.child((cx, "Value: "))
.child((cx, move || value.get()))
.child((cx, "!")),
))
.child((
cx,
button(cx)
.on(ev::click, move |_| set_value.update(|value| *value += step))
.child((cx, "+1")),
))
}

View File

@@ -0,0 +1,16 @@
use counter_without_macros as counter;
use leptos::*;
pub fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|cx| {
counter::view(
cx,
counter::Props {
initial_value: 0,
step: 1,
},
)
})
}

View File

@@ -0,0 +1,58 @@
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
use counter_without_macros as counter;
use leptos::*;
use web_sys::HtmlElement;
#[wasm_bindgen_test]
fn inc() {
mount_to_body(|cx| {
counter::view(
cx,
counter::Props {
initial_value: 0,
step: 1,
},
)
});
let document = leptos::document();
let div = document.query_selector("div").unwrap().unwrap();
let clear = div
.first_child()
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
let dec = clear
.next_sibling()
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
let text = dec
.next_sibling()
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
let inc = text
.next_sibling()
.unwrap()
.dyn_into::<HtmlElement>()
.unwrap();
inc.click();
inc.click();
assert_eq!(text.text_content(), Some("Value: 2!".to_string()));
dec.click();
dec.click();
dec.click();
dec.click();
assert_eq!(text.text_content(), Some("Value: -2!".to_string()));
clear.click();
assert_eq!(text.text_content(), Some("Value: 0!".to_string()));
}

View File

@@ -0,0 +1,9 @@
# Leptos Counters Example
This example showcases a basic leptos app with many counters. It is a good example of how to setup a basic reactive app with signals and effects, and how to interact with browser events.
## Client Side Rendering
To run it as a client-side app, you can issue `trunk serve --open` in the root. This will build the entire app into one CSR bundle.
> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/)

View File

@@ -97,10 +97,10 @@ fn Counter(
<li>
<button on:click=move |_| set_value.update(move |value| *value -= 1)>"-1"</button>
<input type="text"
prop:value={move || value().to_string()}
prop:value={value}
on:input=input
/>
<span>{move || value().to_string()}</span>
<span>{value}</span>
<button on:click=move |_| set_value.update(move |value| *value += 1)>"+1"</button>
<button on:click=move |_| set_counters.update(move |counters| counters.retain(|(counter_id, _)| counter_id != &id))>"x"</button>
</li>

View File

@@ -0,0 +1,9 @@
# Leptos Counters Example on Rust Stable
This example showcases a basic Leptos app with many counters. It is a good example of how to setup a basic reactive app with signals and effects, and how to interact with browser events. Unlike the other counters example, it will compile on Rust stable, because it has the `stable` feature enabled.
## Client Side Rendering
To run it as a client-side app, you can issue `trunk serve --open` in the root. This will build the entire app into one CSR bundle.
> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/)

View File

@@ -0,0 +1,94 @@
[package]
name = "errors_axum"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
anyhow = "1.0.66"
console_log = "0.2.0"
console_error_panic_hook = "0.1.7"
futures = "0.3.25"
cfg-if = "1.0.0"
leptos = { path = "../../../leptos/leptos", default-features = false, features = [
"serde",
] }
leptos_axum = { path = "../../../leptos/integrations/axum", default-features = false, optional = true }
leptos_meta = { path = "../../../leptos/meta", default-features = false }
leptos_router = { path = "../../../leptos/router", default-features = false }
leptos_reactive = { path = "../../../leptos/leptos_reactive", default-features = false }
log = "0.4.17"
simple_logger = "4.0.0"
serde = { version = "1.0.148", features = ["derive"] }
serde_json = "1.0.89"
gloo-net = { version = "0.2.5", features = ["http"] }
reqwest = { version = "0.11.13", features = ["json"] }
axum = { version = "0.6.1", optional = true }
tower = { version = "0.4.13", optional = true }
tower-http = { version = "0.3.4", features = ["fs"], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true }
http = { version = "0.2.8" }
thiserror = "1.0.38"
tracing = "0.1.37"
[features]
default = ["csr"]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
ssr = ["dep:axum", "dep:tower", "dep:tower-http", "dep:tokio", "leptos/ssr", "leptos_meta/ssr", "leptos_router/ssr", "dep:leptos_axum"]
[package.metadata.cargo-all-features]
denylist = [
"axum",
"tower",
"tower-http",
"tokio",
"leptos_axum",
]
skip_feature_sets = [["csr", "ssr"], ["csr", "hydrate"], ["ssr", "hydrate"]]
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name = "errors_axum"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
site-root = "target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
# Defaults to pkg
site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
style-file = "./style.css"
# [Optional] Files in the asset-dir will be copied to the site-root directory
assets-dir = "public"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
end2end-cmd = "npx playwright test"
# The browserlist query used for optimizing the CSS.
browserquery = "defaults"
# Set by cargo-leptos watch when building with tha tool. Controls whether autoreload JS will be included in the head
watch = false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env = "DEV"
# The features to use when compiling the bin target
#
# Optional. Can be over-ridden with the command line parameter --bin-features
bin-features = ["ssr"]
# If the --no-default-features flag should be used when compiling the bin target
#
# Optional. Defaults to false.
bin-default-features = false
# The features to use when compiling the lib target
#
# Optional. Can be over-ridden with the command line parameter --lib-features
lib-features = ["hydrate"]
# If the --no-default-features flag should be used when compiling the lib target
#
# Optional. Defaults to false.
lib-default-features = false

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Greg Johnston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,4 @@
[tasks.build]
command = "cargo"
args = ["+nightly", "build-all-features"]
install_crate = "cargo-all-features"

View File

@@ -0,0 +1,42 @@
# Leptos Todo App Sqlite with Axum
This example creates a basic todo app with an Axum backend that uses Leptos' server functions to call sqlx from the client and seamlessly run it on the server.
## Client Side Rendering
This example cannot be built as a trunk standalone CSR-only app as it requires the server to send HTTP Status Codes.
## Server Side Rendering with cargo-leptos
cargo-leptos is now the easiest and most featureful way to build server side rendered apps with hydration. It provides automatic recompilation of client and server code, wasm optimisation, CSS minification, and more! Check out more about it [here](https://github.com/akesson/cargo-leptos)
1. Install cargo-leptos
```bash
cargo install --locked cargo-leptos
```
2. Build the site in watch mode, recompiling on file changes
```bash
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
3. When ready to deploy, run
```bash
cargo leptos build --release
```
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
1. Install wasm-pack
```bash
cargo install wasm-pack
```
2. Build the Webassembly used to hydrate the HTML from the server
```bash
wasm-pack build --target=web --debug --no-default-features --features=hydrate
```
3. Run the server to serve the Webassembly, JS, and HTML
```bash
cargo run --no-default-features --features=ssr
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,66 @@
use crate::errors::AppError;
use cfg_if::cfg_if;
use leptos::Errors;
use leptos::{
component, create_rw_signal, use_context, view, For, ForProps, IntoView, RwSignal, Scope,
};
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;
// A basic function to display errors served by the error boundaries. Feel free to do more complicated things
// here than just displaying them
#[component]
pub fn ErrorTemplate(
cx: Scope,
#[prop(optional)] outside_errors: Option<Errors>,
#[prop(optional)] errors: Option<RwSignal<Errors>>,
) -> impl IntoView {
let errors = match outside_errors {
Some(e) => create_rw_signal(cx, e),
None => match errors {
Some(e) => e,
None => panic!("No Errors found and we expected errors!"),
},
};
// Get Errors from Signal
let errors = errors.get().0;
// Downcast lets us take a type that implements `std::error::Error`
let errors: Vec<AppError> = errors
.into_iter()
.filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned())
.collect();
println!("Errors: {errors:#?}");
// Only the response code for the first error is actually sent from the server
// this may be customized by the specific application
cfg_if! {
if #[cfg(feature="ssr")]{
let response = use_context::<ResponseOptions>(cx);
if let Some(response) = response{
response.set_status(errors[0].status_code());
}
}
}
view! {cx,
<h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1>
<For
// a function that returns the items we're iterating over; a signal is fine
each= move || {errors.clone().into_iter().enumerate()}
// a unique key for each item as a reference
key=|(index, _error)| *index
// renders each item to a view
view= move |error| {
let error_string = error.1.to_string();
let error_code= error.1.status_code();
view! {
cx,
<h2>{error_code.to_string()}</h2>
<p>"Error: " {error_string}</p>
}
}
/>
}
}

View File

@@ -0,0 +1,19 @@
use http::status::StatusCode;
use thiserror::Error;
#[derive(Debug, Clone, Error)]
pub enum AppError {
#[error("Not Found")]
NotFound,
#[error("Internal Server Error")]
InternalServerError,
}
impl AppError {
pub fn status_code(&self) -> StatusCode {
match self {
AppError::NotFound => StatusCode::NOT_FOUND,
AppError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

View File

@@ -0,0 +1,49 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use std::sync::Arc;
use leptos::{LeptosOptions, Errors, view};
use crate::error_template::{ErrorTemplate, ErrorTemplateProps};
use crate::errors::AppError;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let mut errors = Errors::default();
errors.insert_with_default_key(AppError::NotFound);
let handler = leptos_axum::render_app_to_stream(options.to_owned(), move |cx| view!{cx, <ErrorTemplate outside_errors=errors.clone()/>});
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}
}
}

View File

@@ -0,0 +1,80 @@
use crate::{
error_template::{ErrorTemplate, ErrorTemplateProps},
errors::AppError,
};
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
cfg_if! { if #[cfg(feature = "ssr")] {
pub fn register_server_functions() {
_ = CauseInternalServerError::register();
_ = CauseNotFoundError::register();
}
}}
#[server(CauseInternalServerError, "/api")]
pub async fn cause_internal_server_error() -> Result<(), ServerFnError> {
// fake API delay
std::thread::sleep(std::time::Duration::from_millis(1250));
Err(ServerFnError::ServerError(
"Generic Server Error".to_string(),
))
}
#[server(CauseNotFoundError, "/api")]
pub async fn cause_not_found_error() -> Result<(), ServerFnError> {
Err(ServerFnError::ServerError("Not Found".to_string()))
}
#[component]
pub fn App(cx: Scope) -> impl IntoView {
//let id = use_context::<String>(cx);
provide_meta_context(cx);
view! {
cx,
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Stylesheet id="leptos" href="/pkg/errors_axum.css"/>
<Router>
<header>
<h1>"Error Examples:"</h1>
</header>
<main>
<Routes>
<Route path="" view=|cx| view! {
cx,
<ErrorBoundary fallback=|cx, errors| view!{cx, <ErrorTemplate errors=errors/>}>
<ExampleErrors/>
</ErrorBoundary>
}/>
</Routes>
</main>
</Router>
}
}
#[component]
pub fn ExampleErrors(cx: Scope) -> impl IntoView {
view! {
cx,
<p>
"This link will load a 404 page since it does not exist. Verify with browser development tools:"
<a href="/404">"This Page Does not Exist"</a>
</p>
<p>
"The following <div> will always contain an error and cause the page to produce status 500. Check browser dev tools. "
</p>
<div>
<ErrorBoundary fallback=|cx, errors| view!{cx, <ErrorTemplate errors=errors/>}>
<ReturnsError/>
</ErrorBoundary>
</div>
}
}
#[component]
pub fn ReturnsError(_cx: Scope) -> impl IntoView {
Err::<String, AppError>(AppError::InternalServerError)
}

View File

@@ -0,0 +1,25 @@
use cfg_if::cfg_if;
use leptos::*;
pub mod error_template;
pub mod errors;
pub mod fallback;
pub mod landing;
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
use crate::landing::*;
#[wasm_bindgen]
pub fn hydrate() {
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(|cx| {
view! { cx, <App/> }
});
}
}
}

View File

@@ -0,0 +1,74 @@
use cfg_if::cfg_if;
use leptos::*;
// boilerplate to run in different modes
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
routing::{post, get},
extract::{Extension, Path},
http::Request,
response::{IntoResponse, Response},
Router,
};
use axum::body::Body as AxumBody;
use crate::landing::*;
use errors_axum::*;
use crate::fallback::file_and_error_handler;
use leptos_axum::{generate_route_list, LeptosRoutes};
use std::sync::Arc;
//Define a handler to test extractor with state
async fn custom_handler(Path(id): Path<String>, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<AxumBody>) -> Response{
let handler = leptos_axum::render_app_to_stream_with_context((*options).clone(),
move |cx| {
provide_context(cx, id.clone());
},
|cx| view! { cx, <App/> }
);
handler(req).await.into_response()
}
#[tokio::main]
async fn main() {
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
crate::landing::register_server_functions();
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_address;
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
// build our application with a route
let app = Router::new()
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
.route("/special/:id", get(custom_handler))
.leptos_routes(leptos_options.clone(), routes, |cx| view! { cx, <App/> } )
.fallback(file_and_error_handler)
.layer(Extension(Arc::new(leptos_options)));
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
}
// client-only stuff for Trunk
else {
use todo_app_sqlite_axum::landing::*;
pub fn main() {
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|cx| {
view! { cx, <App/> }
});
}
}
}

View File

@@ -0,0 +1,3 @@
.pending {
color: purple;
}

9
examples/fetch/README.md Normal file
View File

@@ -0,0 +1,9 @@
# Client Side Fetch
This example shows how to fetch data from the client in WebAssembly.
## Client Side Rendering
To run it as a client-side app, you can issue `trunk serve --open` in the root. This will build the entire app into one CSR bundle.
> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/)

View File

@@ -2,6 +2,7 @@
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
</head>
<style>
img {

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -6,7 +6,7 @@ const APP_ID: &str = "dev.leptos.Counter";
// Basic GTK app setup from https://gtk-rs.org/gtk4-rs/stable/latest/book/hello_world.html
fn main() {
_ = create_scope(|cx| {
_ = create_scope(create_runtime(), |cx| {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();

View File

@@ -19,7 +19,7 @@ leptos_meta = { path = "../../meta", default-features = false }
leptos_actix = { path = "../../integrations/actix", default-features = false, optional = true }
leptos_router = { path = "../../router", default-features = false }
log = "0.4"
simple_logger = "2"
simple_logger = "4.0.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
gloo-net = { version = "0.2", features = ["http"] }
@@ -56,9 +56,9 @@ site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
style-file = "./style.css"
# [Optional] Files in the asset-dir will be copied to the site-root directory
# assets-dir = "static/assets"
assets-dir = "public"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-address = "127.0.0.1:3000"
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.

View File

@@ -17,6 +17,9 @@ cargo install --locked cargo-leptos
```bash
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
3. When ready to deploy, run
```bash
cargo leptos build --release
@@ -25,7 +28,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes..
1. Install wasm-pack
```bash
cargo install wasm-pack

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="rust" data-wasm-opt="z" data-cargo-features="csr"/>
<link data-trunk rel="css" href="./style.css"/>
</head>
<body></body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +1,4 @@
use leptos::{on_cleanup, Scope, Serializable};
use leptos::{Scope, Serializable};
use serde::{Deserialize, Serialize};
pub fn story(path: &str) -> String {
@@ -29,7 +29,7 @@ where
// abort in-flight requests if the Scope is disposed
// i.e., if we've navigated away from this page
on_cleanup(cx, move || {
leptos::on_cleanup(cx, move || {
if let Some(abort_controller) = abort_controller {
abort_controller.abort()
}

View File

@@ -15,7 +15,8 @@ pub fn App(cx: Scope) -> impl IntoView {
view! {
cx,
<>
<Stylesheet id="leptos" href="/target/site/pkg/hackernews.css"/>
<Stylesheet id="leptos" href="/pkg/hackernews.css"/>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Meta name="description" content="Leptos implementation of a HackerNews demo."/>
<Router>
<Nav />
@@ -23,7 +24,7 @@ pub fn App(cx: Scope) -> impl IntoView {
<Routes>
<Route path="users/:id" view=|cx| view! { cx, <User/> }/>
<Route path="stories/:id" view=|cx| view! { cx, <Story/> }/>
<Route path="*stories" view=|cx| view! { cx, <Stories/> }/>
<Route path=":stories?" view=|cx| view! { cx, <Stories/> }/>
</Routes>
</main>
</Router>

View File

@@ -8,28 +8,36 @@ cfg_if! {
use actix_files::{Files};
use actix_web::*;
use hackernews::{App,AppProps};
use leptos_actix::{LeptosRoutes, generate_route_list};
#[get("/style.css")]
async fn css() -> impl Responder {
actix_files::NamedFile::open_async("./style.css").await
}
#[get("/favicon.ico")]
async fn favicon() -> impl Responder {
actix_files::NamedFile::open_async("./target/site//favicon.ico").await
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|cx| view! { cx, <App/> });
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let pkg_dir = &leptos_options.site_pkg_dir;
let bundle_path = format!("/{site_root}/{pkg_dir}");
App::new()
.service(Files::new("/pkg", "./pkg")) // used by wasm-pack and cargo run. Can be removed if using cargo-leptos
.service(Files::new(&bundle_path, format!("./{bundle_path}"))) // used by cargo-leptos. Can be removed if using wasm-pack and cargo run.
.service(css)
.service(favicon)
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
.route("/{tail:.*}", leptos_actix::render_app_to_stream(leptos_options.to_owned(), |cx| view! { cx, <App/> }))
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), |cx| view! { cx, <App/> })
.service(Files::new("/", &site_root))
//.wrap(middleware::Compress::default())
})
.bind(&addr)?
@@ -38,7 +46,11 @@ cfg_if! {
}
} else {
fn main() {
// no client-side main function
use hackernews::{App, AppProps};
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|cx| view! { cx, <App/> })
}
}
}

View File

@@ -134,7 +134,7 @@ fn Story(cx: Scope, story: api::Story) -> impl IntoView {
view! { cx,
<span>
{"by "}
{story.user.map(|user| view ! { cx, <A href=format!("/users/{}", user)>{user.clone()}</A>})}
{story.user.map(|user| view ! { cx, <A href=format!("/users/{user}")>{user.clone()}</A>})}
{format!(" {} | ", story.time_ago)}
<A href=format!("/stories/{}", story.id)>
{if story.comments_count.unwrap_or_default() > 0 {

View File

@@ -17,7 +17,7 @@ pub fn Story(cx: Scope) -> impl IntoView {
}
},
);
let meta_description = move || story.read().and_then(|story| story.map(|story| story.title.clone())).unwrap_or_else(|| "Loading story...".to_string());
let meta_description = move || story.read().and_then(|story| story.map(|story| story.title)).unwrap_or_else(|| "Loading story...".to_string());
view! { cx,
<>
@@ -37,7 +37,7 @@ pub fn Story(cx: Scope) -> impl IntoView {
{story.user.map(|user| view! { cx, <p class="meta">
{story.points}
" points | by "
<A href=format!("/users/{}", user)>{user.clone()}</A>
<A href=format!("/users/{user}")>{user.clone()}</A>
{format!(" {}", story.time_ago)}
</p>})}
</div>

View File

@@ -64,9 +64,9 @@ site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
style-file = "./style.css"
# [Optional] Files in the asset-dir will be copied to the site-root directory
# assets-dir = "static/assets"
assets-dir = "public"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-address = "127.0.0.1:3000"
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.

View File

@@ -17,6 +17,9 @@ cargo install --locked cargo-leptos
```bash
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
3. When ready to deploy, run
```bash
cargo leptos build --release
@@ -25,7 +28,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes..
1. Install wasm-pack
```bash
cargo install wasm-pack

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,28 @@
use leptos::Errors;
use leptos::{view, For, ForProps, IntoView, RwSignal, Scope, View};
// A basic function to display errors served by the error boundaries. Feel free to do more complicated things
// here than just displaying them
pub fn error_template(cx: Scope, errors: Option<RwSignal<Errors>>) -> View {
let Some(errors) = errors else {
panic!("No Errors found and we expected errors!");
};
view! {cx,
<h1>"Errors"</h1>
<For
// a function that returns the items we're iterating over; a signal is fine
each= move || {errors.get().0.into_iter()}
// a unique key for each item as a reference
key=|error| error.0.clone()
// renders each item to a view
view= move |error| {
let error_string = error.1.to_string();
view! {
cx,
<p>"Error: " {error_string}</p>
}
}
/>
}
.into_view(cx)
}

View File

@@ -0,0 +1,47 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::{boxed, Body, BoxBody},
extract::Extension,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use std::sync::Arc;
use leptos::{LeptosOptions};
use crate::error_template::error_template;
pub async fn file_and_error_handler(uri: Uri, Extension(options): Extension<Arc<LeptosOptions>>, req: Request<Body>) -> AxumResponse {
let options = &*options;
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let handler = leptos_axum::render_app_to_stream(options.to_owned(), |cx| error_template(cx, None));
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
let root_path = format!("{root}");
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(&root_path).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
}
}
}

View File

@@ -11,7 +11,6 @@ if #[cfg(feature = "ssr")] {
pub async fn file_handler(uri: Uri) -> Result<Response<BoxBody>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/pkg").await?;
println!("FIRST URI{:?}", uri);
if res.status() == StatusCode::NOT_FOUND {
// try with `.html`
@@ -27,7 +26,6 @@ if #[cfg(feature = "ssr")] {
pub async fn get_static_file_handler(uri: Uri) -> Result<Response<BoxBody>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/static").await?;
println!("FIRST URI{:?}", uri);
if res.status() == StatusCode::NOT_FOUND {
Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid URI".to_string()))
@@ -41,7 +39,6 @@ if #[cfg(feature = "ssr")] {
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// When run normally, the root should be the crate root
println!("Base: {:#?}", base);
if base == "/static" {
match ServeDir::new("./static").oneshot(req).await {
Ok(res) => Ok(res.map(boxed)),

View File

@@ -3,6 +3,8 @@ use leptos::{component, view, IntoView, Scope};
use leptos_meta::*;
use leptos_router::*;
mod api;
pub mod error_template;
pub mod fallback;
pub mod handlers;
mod routes;
use routes::nav::*;
@@ -16,7 +18,8 @@ pub fn App(cx: Scope) -> impl IntoView {
view! {
cx,
<>
<Stylesheet id="leptos" href="./target/site/pkg/hackernews_axum.css"/>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Stylesheet id="leptos" href="/pkg/hackernews_axum.css"/>
<Meta name="description" content="Leptos implementation of a HackerNews demo."/>
<Router>
<Nav />
@@ -24,7 +27,7 @@ pub fn App(cx: Scope) -> impl IntoView {
<Routes>
<Route path="users/:id" view=|cx| view! { cx, <User/> }/>
<Route path="stories/:id" view=|cx| view! { cx, <Story/> }/>
<Route path="*stories" view=|cx| view! { cx, <Stories/> }/>
<Route path=":stories?" view=|cx| view! { cx, <Stories/> }/>
</Routes>
</main>
</Router>

View File

@@ -6,10 +6,12 @@ cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
Router,
error_handling::HandleError,
routing::get,
extract::Extension,
};
use http::StatusCode;
use tower_http::services::ServeDir;
use leptos_axum::{generate_route_list, LeptosRoutes};
use std::sync::Arc;
use hackernews_axum::fallback::file_and_error_handler;
#[tokio::main]
async fn main() {
@@ -17,40 +19,17 @@ if #[cfg(feature = "ssr")] {
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
let site_root = &leptos_options.site_root;
let pkg_dir = &leptos_options.site_pkg_dir;
// The URL path of the generated JS/WASM bundle from cargo-leptos
let bundle_path = format!("/{site_root}/{pkg_dir}");
// The filesystem path of the generated JS/WASM bundle from cargo-leptos
let bundle_filepath = format!("./{site_root}/{pkg_dir}");
let addr = leptos_options.site_address.clone();
log::debug!("serving at {addr}");
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
// These are Tower Services that will serve files from the static and pkg repos.
// HandleError is needed as Axum requires services to implement Infallible Errors
// because all Errors are converted into Responses
let static_service = HandleError::new( ServeDir::new("./static"), handle_file_error);
let pkg_service =HandleError::new( ServeDir::new("./pkg"), handle_file_error);
let cargo_leptos_service = HandleError::new( ServeDir::new(&bundle_filepath), handle_file_error);
/// Convert the Errors from ServeDir to a type that implements IntoResponse
async fn handle_file_error(err: std::io::Error) -> (StatusCode, String) {
(
StatusCode::NOT_FOUND,
format!("File Not Found: {}", err),
)
}
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.nest_service("/pkg", pkg_service) // Only need if using wasm-pack. Can be deleted if using cargo-leptos
.nest_service(&bundle_path, cargo_leptos_service) // Only needed if using cargo-leptos. Can be deleted if using wasm-pack and cargo-run
.nest_service("/static", static_service)
.fallback(leptos_axum::render_app_to_stream(leptos_options, |cx| view! { cx, <App/> }));
.route("/favicon.ico", get(file_and_error_handler))
.leptos_routes(leptos_options.clone(), routes, |cx| view! { cx, <App/> } )
.fallback(file_and_error_handler)
.layer(Extension(Arc::new(leptos_options)));
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`

View File

@@ -134,7 +134,7 @@ fn Story(cx: Scope, story: api::Story) -> impl IntoView {
view! { cx,
<span>
{"by "}
{story.user.map(|user| view ! { cx, <A href=format!("/users/{}", user)>{user.clone()}</A>})}
{story.user.map(|user| view ! { cx, <A href=format!("/users/{user}")>{user.clone()}</A>})}
{format!(" {} | ", story.time_ago)}
<A href=format!("/stories/{}", story.id)>
{if story.comments_count.unwrap_or_default() > 0 {

View File

@@ -17,7 +17,7 @@ pub fn Story(cx: Scope) -> impl IntoView {
}
},
);
let meta_description = move || story.read().and_then(|story| story.map(|story| story.title.clone())).unwrap_or_else(|| "Loading story...".to_string());
let meta_description = move || story.read().and_then(|story| story.map(|story| story.title)).unwrap_or_else(|| "Loading story...".to_string());
view! { cx,
<>
@@ -37,7 +37,7 @@ pub fn Story(cx: Scope) -> impl IntoView {
{story.user.map(|user| view! { cx, <p class="meta">
{story.points}
" points | by "
<A href=format!("/users/{}", user)>{user.clone()}</A>
<A href=format!("/users/{user}")>{user.clone()}</A>
{format!(" {}", story.time_ago)}
</p>})}
</div>

View File

@@ -1,326 +0,0 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
font-size: 15px;
background-color: #f2f3f5;
margin: 0;
padding-top: 55px;
color: #34495e;
overflow-y: scroll
}
a {
color: #34495e;
text-decoration: none
}
.header {
background-color: #335d92;
position: fixed;
z-index: 999;
height: 55px;
top: 0;
left: 0;
right: 0
}
.header .inner {
max-width: 800px;
box-sizing: border-box;
margin: 0 auto;
padding: 15px 5px
}
.header a {
color: rgba(255, 255, 255, .8);
line-height: 24px;
transition: color .15s ease;
display: inline-block;
vertical-align: middle;
font-weight: 300;
letter-spacing: .075em;
margin-right: 1.8em
}
.header a:hover {
color: #fff
}
.header a.active {
color: #fff;
font-weight: 400
}
.header a:nth-child(6) {
margin-right: 0
}
.header .github {
color: #fff;
font-size: .9em;
margin: 0;
float: right
}
.logo {
width: 24px;
margin-right: 10px;
display: inline-block;
vertical-align: middle
}
.view {
max-width: 800px;
margin: 0 auto;
position: relative
}
.fade-enter-active,
.fade-exit-active {
transition: all .2s ease
}
.fade-enter,
.fade-exit-active {
opacity: 0
}
@media (max-width:860px) {
.header .inner {
padding: 15px 30px
}
}
@media (max-width:600px) {
.header .inner {
padding: 15px
}
.header a {
margin-right: 1em
}
.header .github {
display: none
}
}
.news-view {
padding-top: 45px
}
.news-list,
.news-list-nav {
background-color: #fff;
border-radius: 2px
}
.news-list-nav {
padding: 15px 30px;
position: fixed;
text-align: center;
top: 55px;
left: 0;
right: 0;
z-index: 998;
box-shadow: 0 1px 2px rgba(0, 0, 0, .1)
}
.news-list-nav .page-link {
margin: 0 1em
}
.news-list-nav .disabled {
color: #aaa
}
.news-list {
position: absolute;
margin: 30px 0;
width: 100%;
transition: all .5s cubic-bezier(.55, 0, .1, 1)
}
.news-list ul {
list-style-type: none;
padding: 0;
margin: 0
}
@media (max-width:600px) {
.news-list {
margin: 10px 0
}
}
.news-item {
background-color: #fff;
padding: 20px 30px 20px 80px;
border-bottom: 1px solid #eee;
position: relative;
line-height: 20px
}
.news-item .score {
color: #335d92;
font-size: 1.1em;
font-weight: 700;
position: absolute;
top: 50%;
left: 0;
width: 80px;
text-align: center;
margin-top: -10px
}
.news-item .host,
.news-item .meta {
font-size: .85em;
color: #626262
}
.news-item .host a,
.news-item .meta a {
color: #626262;
text-decoration: underline
}
.news-item .host a:hover,
.news-item .meta a:hover {
color: #335d92
}
.item-view-header {
background-color: #fff;
padding: 1.8em 2em 1em;
box-shadow: 0 1px 2px rgba(0, 0, 0, .1)
}
.item-view-header h1 {
display: inline;
font-size: 1.5em;
margin: 0;
margin-right: .5em
}
.item-view-header .host,
.item-view-header .meta,
.item-view-header .meta a {
color: #626262
}
.item-view-header .meta a {
text-decoration: underline
}
.item-view-comments {
background-color: #fff;
margin-top: 10px;
padding: 0 2em .5em
}
.item-view-comments-header {
margin: 0;
font-size: 1.1em;
padding: 1em 0;
position: relative
}
.item-view-comments-header .spinner {
display: inline-block;
margin: -15px 0
}
.comment-children {
list-style-type: none;
padding: 0;
margin: 0
}
@media (max-width:600px) {
.item-view-header h1 {
font-size: 1.25em
}
}
.comment-children .comment-children {
margin-left: 1.5em
}
.comment {
border-top: 1px solid #eee;
position: relative
}
.comment .by,
.comment .text,
.comment .toggle {
font-size: .9em;
margin: 1em 0
}
.comment .by {
color: #626262
}
.comment .by a {
color: #626262;
text-decoration: underline
}
.comment .text {
overflow-wrap: break-word
}
.comment .text a:hover {
color: #335d92
}
.comment .text pre {
white-space: pre-wrap
}
.comment .toggle {
background-color: #fffbf2;
padding: .3em .5em;
border-radius: 4px
}
.comment .toggle a {
color: #626262;
cursor: pointer
}
.comment .toggle.open {
padding: 0;
background-color: transparent;
margin-bottom: -.5em
}
.user-view {
background-color: #fff;
box-sizing: border-box;
padding: 2em 3em
}
.user-view h1 {
margin: 0;
font-size: 1.5em
}
.user-view .meta {
list-style-type: none;
padding: 0
}
.user-view .label {
display: inline-block;
min-width: 4em
}
.user-view .about {
margin: 1em 0
}
.user-view .links a {
text-decoration: underline
}

View File

@@ -0,0 +1,17 @@
# Parent Child Example
This example highlights four different ways that child components can communicate with their parent:
1. `<ButtonA/>`: passing a WriteSignal as one of the child component props,
for the child component to write into and the parent to read
2. `<ButtonB/>`: passing a closure as one of the child component props, for
the child component to call
3. `<ButtonC/>`: adding a simple event listener on the child component itself
4. `<ButtonD/>`: providing a context that is used in the component (rather than prop drilling)
## Client Side Rendering
To run it as a Client Side App, you can issue `trunk serve --open` in the root. This will build the entire
app into one CSR bundle
> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/)

View File

@@ -2,6 +2,7 @@
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
<style>
.red {
color: red;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,8 +1,11 @@
# Leptos Router Example
This example demonstrates how Leptos' router works
This example demonstrates how Leptoss router works for client side routing.
## Build and Run it
## Run it
```bash
trunk serve --open
```
> If you don't have `trunk` installed, [click here for install instructions.](https://trunkrs.dev/)

View File

@@ -2,6 +2,7 @@
<html>
<head>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
<style>
a[aria-current] {
font-weight: bold;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -12,6 +12,11 @@ pub fn RouterExample(cx: Scope) -> impl IntoView {
view! { cx,
<Router>
<nav>
// ordinary <a> elements can be used for client-side navigation
// using <A> has two effects:
// 1) ensuring that relative routing works properly for nested routes
// 2) setting the `aria-current` attribute on the current link,
// for a11y and styling purposes
<A exact=true href="/">"Contacts"</A>
<A href="about">"About"</A>
<A href="settings">"Settings"</A>
@@ -105,12 +110,15 @@ pub fn Contact(cx: Scope) -> impl IntoView {
// Some(None) => has loaded and found no contact
Some(None) => Some(view! { cx, <p>"No contact with this ID was found."</p> }.into_any()),
// Some(Some) => has loaded and found a contact
Some(Some(contact)) => Some(view! { cx,
<section class="card">
<h1>{contact.first_name} " " {contact.last_name}</h1>
<p>{contact.address_1}<br/>{contact.address_2}</p>
</section>
}.into_any()),
Some(Some(contact)) => Some(
view! { cx,
<section class="card">
<h1>{contact.first_name} " " {contact.last_name}</h1>
<p>{contact.address_1}<br/>{contact.address_2}</p>
</section>
}
.into_any(),
),
};
view! { cx,
@@ -125,9 +133,18 @@ pub fn Contact(cx: Scope) -> impl IntoView {
#[component]
pub fn About(cx: Scope) -> impl IntoView {
log::debug!("rendering <About/>");
// use_navigate allows you to navigate programmatically by calling a function
let navigate = use_navigate(cx);
view! { cx,
<>
// note: this is just an illustration of how to use `use_navigate`
// <button on:click> to navigate is an *anti-pattern*
// you should ordinarily use a link instead,
// both semantically and so your link will work before WASM loads
<button on:click=move |_| { _ = navigate("/", Default::default()); }>
"Home"
</button>
<h1>"About"</h1>
<p>"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."</p>
</>

View File

@@ -87,11 +87,11 @@ site-root = "target/site"
# Defaults to pkg
site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
# style-file = "src/styles/tailwind.css"
style-file = "style/output.css"
# [Optional] Files in the asset-dir will be copied to the site-root directory
assets-dir = "assets"
assets-dir = "public"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-address = "127.0.0.1:3000"
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.

View File

@@ -1,6 +1,6 @@
# Leptos Starter Template
This is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/gbj/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
This is a template demonstrating how to integrate [TailwindCSS](https://tailwindcss.com/) with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
If you don't have `cargo-leptos` installed you can install it with
@@ -16,6 +16,8 @@ and
in this directory.
Open browser on [http://localhost:3000/](http://localhost:3000/)
You can begin editing your app at `src/app.rs`.
## Installing Tailwind
@@ -52,13 +54,11 @@ If you're using VS Code, add the following to your `settings.json`
"css.validate": false,
```
Install [Tailwind CSS Intellisense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss).
Install "VS Browser" extension, a browser at the right window.
Allow vscode Ports forward: 3000, 3001.
## Notes about Tooling
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
@@ -72,8 +72,8 @@ By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If
## Alternatives to cargo-leptos
This crate can be run without `cargo-leptos`, using `wasm-pack` and `cargo`. To do so, you'll need to install some other tools.
1. `cargo install wasm-pack`
0. `cargo install wasm-pack`
1. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
### Server Side Rendering With Hydration
@@ -91,15 +91,16 @@ Then run the server with `cargo run` to serve the server side rendered HTML and
cargo run --no-default-features --features=ssr
```
> Note that if your hydration code changes, you will have to rerun the wasm-pack command above before running
> Note that if your hydration code changes, you will have to rerun the wasm-pack command above before running
> `cargo run`
### Client Side Rendering
You'll need to install trunk to client side render this bundle.
1. `cargo install trunk`
Then the site can be served with `trunk serve --open`
Then the site can be served with `trunk serve --open`
## Attribution
Many thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/gbj/leptos/discussions/125).
Many thanks to GreatGreg for putting together this guide. You can find the original, with added details, [here](https://github.com/leptos-rs/leptos/discussions/125).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,28 +1,37 @@
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
#[component]
pub fn App(cx: Scope) -> impl IntoView {
provide_meta_context(cx);
let (count, set_count) = create_signal(cx, 0);
view! {
cx,
<main class="my-0 mx-auto max-w-3xl text-center">
<Stylesheet id="leptos" href="/style.css"/>
<h2 class="p-6 text-4xl">"Welcome to Leptos with Tailwind"</h2>
<p class="px-10 pb-10 text-left">"Tailwind will scan your Rust files for Tailwind class names and compile them into a CSS file."</p>
<button
class="bg-sky-600 hover:bg-sky-700 px-5 py-3 text-white rounded-lg"
on:click=move |_| set_count.update(|count| *count += 1)
>
{move || if count() == 0 {
"Click me!".to_string()
} else {
count().to_string()
}}
</button>
</main>
<Stylesheet id="leptos" href="/pkg/tailwind.css"/>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Router>
<Routes>
<Route path="" view= move |cx| view! {
cx,
<main class="my-0 mx-auto max-w-3xl text-center">
<h2 class="p-6 text-4xl">"Welcome to Leptos with Tailwind"</h2>
<p class="px-10 pb-10 text-left">"Tailwind will scan your Rust files for Tailwind class names and compile them into a CSS file."</p>
<button
class="bg-sky-600 hover:bg-sky-700 px-5 py-3 text-white rounded-lg"
on:click=move |_| set_count.update(|count| *count += 1)
>
{move || if count() == 0 {
"Click me!".to_string()
} else {
count().to_string()
}}
</button>
</main>
}/>
</Routes>
</Router>
}
}

View File

@@ -7,6 +7,7 @@ cfg_if! {
use actix_web::*;
use leptos::*;
use crate::app::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
#[get("/style.css")]
async fn css() -> impl Responder {
@@ -15,20 +16,23 @@ cfg_if! {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let pkg_dir = &leptos_options.site_pkg_dir;
let bundle_path = format!("/{site_root}/{pkg_dir}");
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|cx| view! { cx, <App/> });
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let routes = &routes;
App::new()
.service(css)
.service(Files::new("/pkg", "./pkg")) // used by wasm-pack and cargo run. Can be removed if using cargo-leptos
.service(Files::new(&bundle_path, format!("./{bundle_path}"))) // used by cargo-leptos. Can be removed if using wasm-pack and cargo run.
.route("/{tail:.*}", leptos_actix::render_app_to_stream(leptos_options.to_owned(), |cx| view! { cx, <App/> }))
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), |cx| view! { cx, <App/> })
.service(Files::new("/", &site_root))
.wrap(middleware::Compress::default())
})
.bind(&addr)?

View File

@@ -1,5 +1,5 @@
/*
! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com
! tailwindcss v3.1.8 | MIT License | https://tailwindcss.com
*/
/*
@@ -30,7 +30,6 @@
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
*/
html {
@@ -45,8 +44,6 @@ html {
/* 3 */
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */
font-feature-settings: normal;
/* 5 */
}
/*
@@ -413,13 +410,54 @@ video {
height: auto;
}
/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden] {
display: none;
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
}
*, ::before, ::after {
::-webkit-backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;

View File

@@ -58,9 +58,9 @@ site-pkg-dir = "pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
style-file = "./style.css"
# [Optional] Files in the asset-dir will be copied to the site-root directory
# assets-dir = "static/assets"
assets-dir = "public"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-address = "127.0.0.1:3000"
site-addr = "127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port = 3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.

View File

@@ -17,6 +17,9 @@ cargo install --locked cargo-leptos
```bash
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
3. When ready to deploy, run
```bash
cargo leptos build --release
@@ -25,7 +28,7 @@ cargo leptos build --release
## Server Side Rendering without cargo-leptos
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"pkg"`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS file is no longer processed by cargo-leptos. Building to alternative folders is not supported at this time
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. You'll also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
1. Install wasm-pack
```bash
cargo install wasm-pack

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -9,6 +9,7 @@ cfg_if! {
use actix_files::{Files};
use actix_web::*;
use crate::todo::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
#[get("/style.css")]
async fn css() -> impl Responder {
@@ -25,23 +26,25 @@ cfg_if! {
crate::todo::register_server_functions();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let addr = conf.leptos_options.site_address.clone();
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|cx| view! { cx, <TodoApp/> });
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let pkg_dir = &leptos_options.site_pkg_dir;
let bundle_path = format!("/{site_root}/{pkg_dir}");
let routes = &routes;
App::new()
.service(Files::new("/pkg", "./pkg")) // used by wasm-pack and cargo run. Can be removed if using cargo-leptos
.service(Files::new(&bundle_path, format!("./{bundle_path}"))) // used by cargo-leptos. Can be removed if using wasm-pack and cargo run.
.service(css)
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
.route("/{tail:.*}", leptos_actix::render_app_to_stream(leptos_options.to_owned(), |cx| view! { cx, <TodoApp/> }))
//.wrap(middleware::Compress::default())
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), |cx| view! { cx, <TodoApp/> })
.service(Files::new("/", &site_root))
//.wrap(middleware::Compress::default())
})
.bind(addr)?
.run()

Some files were not shown because too many files have changed in this diff Show More