switch to a Document trait and introduce Script/Head/Style/Meta components (#2635)

* switch to a Document trait and introduce Script/Head/Style/Meta components

* Fix desktop title

* Insert special elements into the head during the inital SSR render

* Make all head component attributes optional

* hydrate head elements

* improve the server streaming head warning

* Document fullstack head hydration approach

* deduplicate head elements by href

* move Link into head::Link

* document head components

* add meta and title examples

* Fix a few doc examples

* fix formatting

* Add title to playwright tests

* serde is optional on web, but it is enabled by hydrate

* remove leftover console log
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-07-17 20:54:03 -05:00
1 parent a490a94ae5
commit 176e67e5b7
65 files changed
+1703 -495

No files matched your search

-49
View File
@@ -1,49 +0,0 @@
/// Code for the Dioxus channel used to communicate between the dioxus and javascript code
pub const NATIVE_EVAL_JS: &str = include_str!("./js/native_eval.js");
#[cfg(feature = "webonly")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub struct JSOwner {
_owner: Box<dyn std::any::Any>,
}
#[cfg(feature = "webonly")]
impl JSOwner {
pub fn new(owner: impl std::any::Any) -> Self {
Self {
_owner: Box::new(owner),
}
}
}
#[cfg(feature = "webonly")]
#[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")]
extern "C" {
pub type WebDioxusChannel;
#[wasm_bindgen(constructor)]
pub fn new(owner: JSOwner) -> WebDioxusChannel;
#[wasm_bindgen(method, js_name = "rustSend")]
pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue);
#[wasm_bindgen(method, js_name = "rustRecv")]
pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue;
#[wasm_bindgen(method)]
pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue);
#[wasm_bindgen(method)]
pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue;
#[wasm_bindgen(method)]
pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel;
pub type WeakDioxusChannel;
#[wasm_bindgen(method, js_name = "rustSend")]
pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue);
#[wasm_bindgen(method, js_name = "rustRecv")]
pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue;
}
-1
View File
@@ -1 +0,0 @@
this files are generated - do not edit them!
-1
View File
@@ -1 +0,0 @@
class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}}export{WebDioxusChannel,WeakDioxusChannel,DioxusChannel,Channel};
+1 -1
View File
@@ -1 +1 @@
6628058622237003340
[6449103750905854967, 12029349297046688094, 14626980229647476238, 8716623267269178440, 5336385715226370016, 14456089431355876478, 3589298972260118311, 2745859031945642653, 5638004933879392817]
@@ -1 +0,0 @@
class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class QueryParams{id;data;constructor(id,method,data){this.id=id,this.data={method,data}}}window.__msg_queues=window.__msg_queues||[];window.finalizationRegistry=window.finalizationRegistry||new FinalizationRegistry(({id})=>{window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(id,"drop")}))});window.getQuery=function(request_id){return window.__msg_queues[request_id]};window.createQuery=function(request_id){return new NativeDioxusChannel(request_id)};class NativeDioxusChannel extends DioxusChannel{rust_to_js;request_id;constructor(request_id){super();this.rust_to_js=new Channel,this.request_id=request_id,window.__msg_queues[request_id]=this.weak(),window.finalizationRegistry.register(this,{id:request_id})}async recv(){return await this.rust_to_js.recv()}send(data){window.ipc.postMessage(JSON.stringify({method:"query",params:new QueryParams(this.request_id,"send",data)}))}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){}}export{NativeDioxusChannel};
-3
View File
@@ -24,9 +24,6 @@ pub mod unified_bindings;
#[cfg(feature = "sledgehammer")]
pub use unified_bindings::*;
#[cfg(feature = "eval")]
pub mod eval;
// Common bindings for minimal usage.
#[cfg(all(feature = "minimal_bindings", feature = "webonly"))]
pub mod minimal_bindings {
-108
View File
@@ -1,108 +0,0 @@
// Handle communication between rust and evaluating javascript
export class Channel {
pending: any[];
waiting: ((data: any) => void)[];
constructor() {
this.pending = [];
this.waiting = [];
}
send(data: any) {
// If there's a waiting callback, call it
if (this.waiting.length > 0) {
this.waiting.shift()(data);
return;
}
// Otherwise queue the data
this.pending.push(data);
}
async recv(): Promise<any> {
return new Promise((resolve, _reject) => {
// If data already exists, resolve immediately
if (this.pending.length > 0) {
resolve(this.pending.shift());
return;
}
// Otherwise queue the resolve callback
this.waiting.push(resolve);
});
}
}
export class WeakDioxusChannel {
inner: WeakRef<DioxusChannel>;
constructor(channel: DioxusChannel) {
this.inner = new WeakRef(channel);
}
// Send data from rust to javascript
rustSend(data: any) {
let channel = this.inner.deref();
if (channel) {
channel.rustSend(data);
}
}
// Receive data sent from javascript in rust
async rustRecv(): Promise<any> {
let channel = this.inner.deref();
if (channel) {
return await channel.rustRecv();
}
}
}
export abstract class DioxusChannel {
// Return a weak reference to this channel
weak(): WeakDioxusChannel {
return new WeakDioxusChannel(this);
}
// Send data from rust to javascript
abstract rustSend(data: any): void;
// Receive data sent from javascript in rust
abstract rustRecv(): Promise<any>;
}
export class WebDioxusChannel extends DioxusChannel {
js_to_rust: Channel;
rust_to_js: Channel;
owner: any;
constructor(owner: any) {
super();
this.owner = owner;
this.js_to_rust = new Channel();
this.rust_to_js = new Channel();
}
// Return a weak reference to this channel
weak(): WeakDioxusChannel {
return new WeakDioxusChannel(this);
}
// Receive message from Rust
async recv() {
return await this.rust_to_js.recv();
}
// Send message to rust.
send(data: any) {
this.js_to_rust.send(data);
}
// Send data from rust to javascript
rustSend(data: any) {
this.rust_to_js.send(data);
}
// Receive data sent from javascript in rust
async rustRecv(): Promise<any> {
return await this.js_to_rust.recv();
}
}
@@ -1,86 +0,0 @@
import { Channel, DioxusChannel, WeakDioxusChannel } from "./eval";
// In dioxus desktop, eval needs to use the window object to store global state because we evaluate separate snippets of javascript in the browser
declare global {
interface Window {
__msg_queues: WeakDioxusChannel[];
finalizationRegistry: FinalizationRegistry<{ id: number }>;
getQuery(request_id: number): WeakDioxusChannel;
createQuery(request_id: number): NativeDioxusChannel;
}
}
// A message that can be sent to the desktop renderer about a query
class QueryParams {
id: number;
data: { method: "drop" | "send"; data?: any };
constructor(id: number, method: "drop" | "send", data?: any) {
this.id = id;
this.data = { method, data };
}
}
window.__msg_queues = window.__msg_queues || [];
// In dioxus desktop, eval is copy so we cannot run a drop handler. Instead, the drop handler is run after the channel is garbage collected in the javascript side
window.finalizationRegistry =
window.finalizationRegistry ||
new FinalizationRegistry(({ id }) => {
// @ts-ignore - wry gives us this
window.ipc.postMessage(
JSON.stringify({
method: "query",
params: new QueryParams(id, "drop"),
})
);
});
// Get a query from the global state
window.getQuery = function (request_id: number): WeakDioxusChannel {
return window.__msg_queues[request_id];
};
// Create a new query (and insert it into the global state)
window.createQuery = function (request_id: number): NativeDioxusChannel {
return new NativeDioxusChannel(request_id);
};
export class NativeDioxusChannel extends DioxusChannel {
rust_to_js: Channel;
request_id: number;
constructor(request_id: number) {
super();
this.rust_to_js = new Channel();
this.request_id = request_id;
window.__msg_queues[request_id] = this.weak();
window.finalizationRegistry.register(this, { id: request_id });
}
// Receive message from Rust
async recv() {
return await this.rust_to_js.recv();
}
// Send message to rust.
send(data: any) {
// @ts-ignore - wry gives us this
window.ipc.postMessage(
JSON.stringify({
method: "query",
params: new QueryParams(this.request_id, "send", data),
})
);
}
// Send data from rust to javascript
rustSend(data: any) {
this.rust_to_js.send(data);
}
// Receive data sent from javascript in rust. This is a no-op in the native interpreter because the rust code runs remotely
async rustRecv(): Promise<any> {}
}