Skip to content

Commit

Permalink
Merge branch 'master' into wasi-support-test
Browse files Browse the repository at this point in the history
  • Loading branch information
langyo authored Oct 12, 2024
2 parents 1290325 + b89609c commit 4da0ba3
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ assignees: ''

This is about:
- [ ] A typo
- [ ] Innaccurate/misleading documentation (e.g. technically incorrect advice)
- [ ] Inaccurate/misleading documentation (e.g. technically incorrect advice)
- [ ] Undocumented code
- [ ] Outdated documentation
- [ ] Other
Expand Down
2 changes: 1 addition & 1 deletion examples/file_upload/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ gloo = "0.11"

[dependencies.web-sys]
version = "0.3"
features = ["File", "DragEvent", "DataTransfer"]
features = ["DataTransfer"]
79 changes: 29 additions & 50 deletions examples/file_upload/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ use std::collections::HashMap;
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use gloo::file::callbacks::FileReader;
use gloo::file::File;
use web_sys::{DragEvent, Event, FileList, HtmlInputElement};
use web_sys::{DragEvent, Event, HtmlInputElement};
use yew::html::TargetCast;
use yew::{html, Callback, Component, Context, Html};

struct FileDetails {
pub struct FileDetails {
name: String,
file_type: String,
data: Vec<u8>,
}

pub enum Msg {
Loaded(String, String, Vec<u8>),
Files(Vec<File>),
Loaded(FileDetails),
Files(Option<web_sys::FileList>),
}

pub struct App {
Expand All @@ -38,40 +37,38 @@ impl Component for App {

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Loaded(file_name, file_type, data) => {
self.files.push(FileDetails {
data,
file_type,
name: file_name.clone(),
});
self.readers.remove(&file_name);
Msg::Loaded(file) => {
self.readers.remove(&file.name);
self.files.push(file);
true
}
Msg::Files(files) => {
for file in files.into_iter() {
let file_name = file.name();
for file in gloo::file::FileList::from(files.expect("files")).iter() {
let link = ctx.link().clone();
let name = file.name().clone();
let file_type = file.raw_mime_type();

let task = {
let link = ctx.link().clone();
let file_name = file_name.clone();

gloo::file::callbacks::read_as_bytes(&file, move |res| {
link.send_message(Msg::Loaded(
file_name,
gloo::file::callbacks::read_as_bytes(file, move |res| {
link.send_message(Msg::Loaded(FileDetails {
data: res.expect("failed to read file"),
file_type,
res.expect("failed to read file"),
))
name,
}))
})
};
self.readers.insert(file_name, task);
self.readers.insert(file.name(), task);
}
true
}
}
}

fn view(&self, ctx: &Context<Self>) -> Html {
let noop_drag = Callback::from(|e: DragEvent| {
e.prevent_default();
});

html! {
<div id="wrapper">
<p id="title">{ "Upload Your Files To The Cloud" }</p>
Expand All @@ -80,15 +77,10 @@ impl Component for App {
id="drop-container"
ondrop={ctx.link().callback(|event: DragEvent| {
event.prevent_default();
let files = event.data_transfer().unwrap().files();
Self::upload_files(files)
})}
ondragover={Callback::from(|event: DragEvent| {
event.prevent_default();
})}
ondragenter={Callback::from(|event: DragEvent| {
event.prevent_default();
Msg::Files(event.data_transfer().unwrap().files())
})}
ondragover={&noop_drag}
ondragenter={&noop_drag}
>
<i class="fa fa-cloud-upload"></i>
<p>{"Drop your images here or click to select"}</p>
Expand All @@ -101,7 +93,7 @@ impl Component for App {
multiple={true}
onchange={ctx.link().callback(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
Self::upload_files(input.files())
Msg::Files(input.files())
})}
/>
<div id="preview-area">
Expand All @@ -114,37 +106,24 @@ impl Component for App {

impl App {
fn view_file(file: &FileDetails) -> Html {
let file_type = file.file_type.to_string();
let src = format!("data:{};base64,{}", file_type, STANDARD.encode(&file.data));
html! {
<div class="preview-tile">
<p class="preview-name">{ format!("{}", file.name) }</p>
<p class="preview-name">{ &file.name }</p>
<div class="preview-media">
if file.file_type.contains("image") {
<img src={format!("data:{};base64,{}", file.file_type, STANDARD.encode(&file.data))} />
<img src={src} />
} else if file.file_type.contains("video") {
<video controls={true}>
<source src={format!("data:{};base64,{}", file.file_type, STANDARD.encode(&file.data))} type={file.file_type.clone()}/>
<source src={src} type={ file_type }/>
</video>
}
</div>
</div>
}
}

fn upload_files(files: Option<FileList>) -> Msg {
let mut result = Vec::new();

if let Some(files) = files {
let files = js_sys::try_iter(&files)
.unwrap()
.unwrap()
.map(|v| web_sys::File::from(v.unwrap()))
.map(File::from);
result.extend(files);
}
Msg::Files(result)
}
}

fn main() {
yew::Renderer::<App>::new().render();
}
2 changes: 1 addition & 1 deletion examples/node_refs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This example shows two input fields which are automatically focused when hovered

## Concepts

The example uses [Refs](https://yew.rs/docs/concepts/components/refs/) to
The example uses [Refs](https://yew.rs/docs/concepts/function-components/node-refs) to
manipulate the underlying DOM element directly.

## Running
Expand Down
2 changes: 1 addition & 1 deletion examples/ssr_router/src/bin/ssr_router_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async fn render(
Query(queries): Query<HashMap<String, String>>,
State((index_html_before, index_html_after)): State<(String, String)>,
) -> impl IntoResponse {
let url = url.to_string();
let url = url.path().to_owned();

let renderer = yew::ServerRenderer::<ServerApp>::with_props(move || ServerAppProps {
url: url.into(),
Expand Down
16 changes: 13 additions & 3 deletions packages/yew-macro/src/html_tree/html_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@ pub enum HtmlNode {
impl Parse for HtmlNode {
fn parse(input: ParseStream) -> Result<Self> {
let node = if HtmlNode::peek(input.cursor()).is_some() {
let lit: Lit = input.parse()?;
if matches!(lit, Lit::ByteStr(_) | Lit::Byte(_) | Lit::Verbatim(_)) {
return Err(syn::Error::new(lit.span(), "unsupported type"));
let lit = input.parse()?;
match lit {
Lit::ByteStr(lit) => {
return Err(syn::Error::new(
lit.span(),
"byte-strings can't be converted to HTML text
note: remove the `b` prefix or convert this to a `String`",
))
}
Lit::Verbatim(lit) => {
return Err(syn::Error::new(lit.span(), "unsupported literal"))
}
_ => (),
}
HtmlNode::Literal(Box::new(lit))
} else {
Expand Down
26 changes: 16 additions & 10 deletions packages/yew-macro/src/stringify.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::borrow::Cow;
use std::mem::size_of;

use proc_macro2::{Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use syn::spanned::Spanned;
Expand Down Expand Up @@ -75,26 +78,30 @@ impl Stringify for LitStr {
}
}
}

impl Stringify for Lit {
fn try_into_lit(&self) -> Option<LitStr> {
let s = match self {
let mut buf = [0; size_of::<char>()];
let s: Cow<'_, str> = match self {
Lit::Str(v) => return v.try_into_lit(),
Lit::Char(v) => v.value().to_string(),
Lit::Int(v) => v.base10_digits().to_string(),
Lit::Float(v) => v.base10_digits().to_string(),
Lit::Bool(_) | Lit::ByteStr(_) | Lit::Byte(_) | Lit::Verbatim(_) => return None,
_ => unreachable!("unknown Lit"),
Lit::Char(v) => (&*v.value().encode_utf8(&mut buf)).into(),
Lit::Int(v) => v.base10_digits().into(),
Lit::Float(v) => v.base10_digits().into(),
Lit::Bool(v) => if v.value() { "true" } else { "false" }.into(),
Lit::Byte(v) => v.value().to_string().into(),
Lit::Verbatim(_) | Lit::ByteStr(_) => return None,
_ => unreachable!("unknown Lit {:?}", self),
};
Some(LitStr::new(&s, self.span()))
}

fn stringify(&self) -> TokenStream {
self.try_into_lit()
.as_ref()
.map(Stringify::stringify)
.unwrap_or_else(|| stringify_at_runtime(self))
.map_or_else(|| stringify_at_runtime(self), Stringify::stringify)
}
}

impl Stringify for Expr {
fn try_into_lit(&self) -> Option<LitStr> {
if let Expr::Lit(v) = self {
Expand All @@ -107,7 +114,6 @@ impl Stringify for Expr {
fn stringify(&self) -> TokenStream {
self.try_into_lit()
.as_ref()
.map(Stringify::stringify)
.unwrap_or_else(|| stringify_at_runtime(self))
.map_or_else(|| stringify_at_runtime(self), Stringify::stringify)
}
}
18 changes: 4 additions & 14 deletions packages/yew-macro/tests/html_macro/node-fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,15 @@ error: unexpected token, expected `}`
5 | html! { <span>{ "valid" "invalid" }</span> };
| ^^^^^^^^^

error: unsupported type
--> tests/html_macro/node-fail.rs:10:14
|
10 | html! { b'a' };
| ^^^^

error: unsupported type
error: byte-strings can't be converted to HTML text
note: remove the `b` prefix or convert this to a `String`
--> tests/html_macro/node-fail.rs:11:14
|
11 | html! { b"str" };
| ^^^^^^

error: unsupported type
--> tests/html_macro/node-fail.rs:12:22
|
12 | html! { <span>{ b'a' }</span> };
| ^^^^

error: unsupported type
error: byte-strings can't be converted to HTML text
note: remove the `b` prefix or convert this to a `String`
--> tests/html_macro/node-fail.rs:13:22
|
13 | html! { <span>{ b"str" }</span> };
Expand Down
8 changes: 4 additions & 4 deletions packages/yew-macro/tests/html_macro/node-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ pub struct u8;
pub struct usize;

fn main() {
_ = ::yew::html! { "" };
_ = ::yew::html! { b'b' };
_ = ::yew::html! { 'a' };
_ = ::yew::html! { "hello" };
_ = ::yew::html! { "42" };
_ = ::yew::html! { "1.234" };
_ = ::yew::html! { "true" };
_ = ::yew::html! { 42 };
_ = ::yew::html! { 1.234 };
_ = ::yew::html! { true };

_ = ::yew::html! { <span>{ "" }</span> };
_ = ::yew::html! { <span>{ 'a' }</span> };
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-router/src/navigator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl Navigator {
pub(crate) fn prefix_basename<'a>(&self, route_s: &'a str) -> Cow<'a, str> {
match self.basename() {
Some(base) => {
if route_s.is_empty() && route_s.is_empty() {
if base.is_empty() && route_s.is_empty() {
Cow::from("/")
} else {
Cow::from(format!("{base}{route_s}"))
Expand Down
39 changes: 34 additions & 5 deletions packages/yew-router/src/router.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Router Component.
use std::borrow::Cow;
use std::rc::Rc;

use gloo::history::query::Raw;
use yew::prelude::*;
use yew::virtual_dom::AttrValue;

Expand Down Expand Up @@ -72,16 +74,43 @@ fn base_router(props: &RouterProps) -> Html {
basename,
} = props.clone();

let basename = basename.map(|m| strip_slash_suffix(&m).to_owned());
let navigator = Navigator::new(history.clone(), basename.clone());

let old_basename = use_mut_ref(|| Option::<String>::None);
let mut old_basename = old_basename.borrow_mut();
if basename != *old_basename {
// If `old_basename` is `Some`, path is probably prefixed with `old_basename`.
// If `old_basename` is `None`, path may or may not be prefixed with the new `basename`,
// depending on whether this is the first render.
let old_navigator = Navigator::new(
history.clone(),
old_basename.as_ref().or(basename.as_ref()).cloned(),
);
*old_basename = basename.clone();
let location = history.location();
let stripped = old_navigator.strip_basename(Cow::from(location.path()));
let prefixed = navigator.prefix_basename(&stripped);

if prefixed != location.path() {
history
.replace_with_query(prefixed, Raw(location.query_str()))
.unwrap_or_else(|never| match never {});
} else {
// Reaching here is possible if the page loads with the correct path, including the
// initial basename. In that case, the new basename would be stripped and then
// prefixed right back. While replacing the history would probably be harmless,
// we might as well avoid doing it.
}
}

let navi_ctx = NavigatorContext { navigator };

let loc_ctx = use_reducer(|| LocationContext {
location: history.location(),
ctr: 0,
});

let basename = basename.map(|m| strip_slash_suffix(&m).to_string());
let navi_ctx = NavigatorContext {
navigator: Navigator::new(history.clone(), basename),
};

{
let loc_ctx_dispatcher = loc_ctx.dispatcher();

Expand Down
Loading

0 comments on commit 4da0ba3

Please sign in to comment.