mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
feat: Oco
(Owned Clones Once) smart pointer (#1480)
This commit is contained in:
parent
6c3e2fe53e
commit
793c191619
31 changed files with 1020 additions and 228 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,3 +9,5 @@ Cargo.lock
|
|||
.idea
|
||||
.direnv
|
||||
.envrc
|
||||
|
||||
.vscode
|
|
@ -1,10 +1,11 @@
|
|||
use crate::TextProp;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A collection of additional HTML attributes to be applied to an element,
|
||||
/// each of which may or may not be reactive.
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct AdditionalAttributes(pub(crate) Vec<(String, TextProp)>);
|
||||
pub struct AdditionalAttributes(pub(crate) Rc<[(String, TextProp)]>);
|
||||
|
||||
impl<I, T, U> From<I> for AdditionalAttributes
|
||||
where
|
||||
|
@ -22,6 +23,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for AdditionalAttributes {
|
||||
fn default() -> Self {
|
||||
Self([].into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over additional HTML attributes.
|
||||
#[repr(transparent)]
|
||||
pub struct AdditionalAttributesIter<'a>(
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use leptos_reactive::Oco;
|
||||
use std::{fmt::Debug, rc::Rc};
|
||||
|
||||
/// Describes a value that is either a static or a reactive string, i.e.,
|
||||
/// a [`String`], a [`&str`], or a reactive `Fn() -> String`.
|
||||
#[derive(Clone)]
|
||||
pub struct TextProp(Rc<dyn Fn() -> String>);
|
||||
pub struct TextProp(Rc<dyn Fn() -> Oco<'static, str>>);
|
||||
|
||||
impl TextProp {
|
||||
/// Accesses the current value of the property.
|
||||
#[inline(always)]
|
||||
pub fn get(&self) -> String {
|
||||
pub fn get(&self) -> Oco<'static, str> {
|
||||
(self.0)()
|
||||
}
|
||||
}
|
||||
|
@ -21,23 +22,38 @@ impl Debug for TextProp {
|
|||
|
||||
impl From<String> for TextProp {
|
||||
fn from(s: String) -> Self {
|
||||
let s: Oco<'_, str> = Oco::Counted(Rc::from(s));
|
||||
TextProp(Rc::new(move || s.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for TextProp {
|
||||
fn from(s: &str) -> Self {
|
||||
let s = s.to_string();
|
||||
impl From<&'static str> for TextProp {
|
||||
fn from(s: &'static str) -> Self {
|
||||
let s: Oco<'_, str> = s.into();
|
||||
TextProp(Rc::new(move || s.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> From<F> for TextProp
|
||||
impl From<Rc<str>> for TextProp {
|
||||
fn from(s: Rc<str>) -> Self {
|
||||
let s: Oco<'_, str> = s.into();
|
||||
TextProp(Rc::new(move || s.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Oco<'static, str>> for TextProp {
|
||||
fn from(s: Oco<'static, str>) -> Self {
|
||||
TextProp(Rc::new(move || s.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S> From<F> for TextProp
|
||||
where
|
||||
F: Fn() -> String + 'static,
|
||||
F: Fn() -> S + 'static,
|
||||
S: Into<Oco<'static, str>>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(s: F) -> Self {
|
||||
TextProp(Rc::new(s))
|
||||
TextProp(Rc::new(move || s().into()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ pub use dyn_child::*;
|
|||
pub use each::*;
|
||||
pub use errors::*;
|
||||
pub use fragment::*;
|
||||
use leptos_reactive::untrack_with_diagnostics;
|
||||
use leptos_reactive::{untrack_with_diagnostics, Oco};
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use once_cell::unsync::OnceCell;
|
||||
use std::fmt;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use std::rc::Rc;
|
||||
use std::{borrow::Cow, fmt};
|
||||
pub use unit::*;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use wasm_bindgen::JsCast;
|
||||
|
@ -55,7 +55,7 @@ pub struct ComponentRepr {
|
|||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
mounted: Rc<OnceCell<()>>,
|
||||
#[cfg(any(debug_assertions, feature = "ssr"))]
|
||||
pub(crate) name: Cow<'static, str>,
|
||||
pub(crate) name: Oco<'static, str>,
|
||||
#[cfg(debug_assertions)]
|
||||
_opening: Comment,
|
||||
/// The children of the component.
|
||||
|
@ -163,24 +163,24 @@ impl IntoView for ComponentRepr {
|
|||
impl ComponentRepr {
|
||||
/// Creates a new [`Component`].
|
||||
#[inline(always)]
|
||||
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
|
||||
pub fn new(name: impl Into<Oco<'static, str>>) -> Self {
|
||||
Self::new_with_id_concrete(name.into(), HydrationCtx::id())
|
||||
}
|
||||
|
||||
/// Creates a new [`Component`] with the given hydration ID.
|
||||
#[inline(always)]
|
||||
pub fn new_with_id(
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
name: impl Into<Oco<'static, str>>,
|
||||
id: HydrationKey,
|
||||
) -> Self {
|
||||
Self::new_with_id_concrete(name.into(), id)
|
||||
}
|
||||
|
||||
fn new_with_id_concrete(name: Cow<'static, str>, id: HydrationKey) -> Self {
|
||||
fn new_with_id_concrete(name: Oco<'static, str>, id: HydrationKey) -> Self {
|
||||
let markers = (
|
||||
Comment::new(Cow::Owned(format!("</{name}>")), &id, true),
|
||||
Comment::new(format!("</{name}>"), &id, true),
|
||||
#[cfg(debug_assertions)]
|
||||
Comment::new(Cow::Owned(format!("<{name}>")), &id, false),
|
||||
Comment::new(format!("<{name}>"), &id, false),
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -236,7 +236,7 @@ where
|
|||
V: IntoView,
|
||||
{
|
||||
id: HydrationKey,
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
children_fn: F,
|
||||
}
|
||||
|
||||
|
@ -246,7 +246,7 @@ where
|
|||
V: IntoView,
|
||||
{
|
||||
/// Creates a new component.
|
||||
pub fn new(name: impl Into<Cow<'static, str>>, f: F) -> Self {
|
||||
pub fn new(name: impl Into<Oco<'static, str>>, f: F) -> Self {
|
||||
Self {
|
||||
id: HydrationCtx::id(),
|
||||
name: name.into(),
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
Comment, IntoView, View,
|
||||
};
|
||||
use cfg_if::cfg_if;
|
||||
use std::{borrow::Cow, cell::RefCell, fmt, ops::Deref, rc::Rc};
|
||||
use std::{cell::RefCell, fmt, ops::Deref, rc::Rc};
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
use crate::{mount_child, prepare_to_move, unmount_child, MountKind, Mountable, Text};
|
||||
|
@ -83,9 +83,9 @@ impl Mountable for DynChildRepr {
|
|||
impl DynChildRepr {
|
||||
fn new_with_id(id: HydrationKey) -> Self {
|
||||
let markers = (
|
||||
Comment::new(Cow::Borrowed("</DynChild>"), &id, true),
|
||||
Comment::new("</DynChild>", &id, true),
|
||||
#[cfg(debug_assertions)]
|
||||
Comment::new(Cow::Borrowed("<DynChild>"), &id, false),
|
||||
Comment::new("<DynChild>", &id, false),
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use crate::hydration::HydrationKey;
|
||||
use crate::{hydration::HydrationCtx, Comment, CoreComponent, IntoView, View};
|
||||
use leptos_reactive::{as_child_of_current_owner, Disposer};
|
||||
use std::{borrow::Cow, cell::RefCell, fmt, hash::Hash, ops::Deref, rc::Rc};
|
||||
use std::{cell::RefCell, fmt, hash::Hash, ops::Deref, rc::Rc};
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use web::*;
|
||||
|
||||
|
@ -79,9 +79,9 @@ impl Default for EachRepr {
|
|||
let id = HydrationCtx::id();
|
||||
|
||||
let markers = (
|
||||
Comment::new(Cow::Borrowed("</Each>"), &id, true),
|
||||
Comment::new("</Each>", &id, true),
|
||||
#[cfg(debug_assertions)]
|
||||
Comment::new(Cow::Borrowed("<Each>"), &id, false),
|
||||
Comment::new("<Each>", &id, false),
|
||||
);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -224,13 +224,13 @@ impl EachItem {
|
|||
|
||||
let markers = (
|
||||
if needs_closing {
|
||||
Some(Comment::new(Cow::Borrowed("</EachItem>"), &id, true))
|
||||
Some(Comment::new("</EachItem>", &id, true))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(debug_assertions)]
|
||||
if needs_closing {
|
||||
Some(Comment::new(Cow::Borrowed("<EachItem>"), &id, false))
|
||||
Some(Comment::new("<EachItem>", &id, false))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
pub mod typed;
|
||||
|
||||
use std::{borrow::Cow, cell::RefCell, collections::HashSet};
|
||||
use leptos_reactive::Oco;
|
||||
use std::{cell::RefCell, collections::HashSet};
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use wasm_bindgen::{
|
||||
convert::FromWasmAbi, intern, prelude::Closure, JsCast, JsValue,
|
||||
|
@ -8,7 +9,7 @@ use wasm_bindgen::{
|
|||
};
|
||||
|
||||
thread_local! {
|
||||
pub(crate) static GLOBAL_EVENTS: RefCell<HashSet<Cow<'static, str>>> = RefCell::new(HashSet::new());
|
||||
pub(crate) static GLOBAL_EVENTS: RefCell<HashSet<Oco<'static, str>>> = RefCell::new(HashSet::new());
|
||||
}
|
||||
|
||||
// Used in template macro
|
||||
|
@ -47,8 +48,8 @@ pub fn add_event_helper<E: crate::ev::EventDescriptor + 'static>(
|
|||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
pub fn add_event_listener<E>(
|
||||
target: &web_sys::Element,
|
||||
key: Cow<'static, str>,
|
||||
event_name: Cow<'static, str>,
|
||||
key: Oco<'static, str>,
|
||||
event_name: Oco<'static, str>,
|
||||
#[cfg(debug_assertions)] mut cb: Box<dyn FnMut(E)>,
|
||||
#[cfg(not(debug_assertions))] cb: Box<dyn FnMut(E)>,
|
||||
options: &Option<web_sys::AddEventListenerOptions>,
|
||||
|
@ -115,7 +116,7 @@ pub(crate) fn add_event_listener_undelegated<E>(
|
|||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
pub(crate) fn add_delegated_event_listener(
|
||||
key: &str,
|
||||
event_name: Cow<'static, str>,
|
||||
event_name: Oco<'static, str>,
|
||||
options: &Option<web_sys::AddEventListenerOptions>,
|
||||
) {
|
||||
GLOBAL_EVENTS.with(|global_events| {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Types for all DOM events.
|
||||
|
||||
use std::{borrow::Cow, marker::PhantomData};
|
||||
use leptos_reactive::Oco;
|
||||
use std::marker::PhantomData;
|
||||
use wasm_bindgen::convert::FromWasmAbi;
|
||||
|
||||
/// A trait for converting types into [web_sys events](web_sys).
|
||||
|
@ -16,10 +17,10 @@ pub trait EventDescriptor: Clone {
|
|||
const BUBBLES: bool;
|
||||
|
||||
/// The name of the event, such as `click` or `mouseover`.
|
||||
fn name(&self) -> Cow<'static, str>;
|
||||
fn name(&self) -> Oco<'static, str>;
|
||||
|
||||
/// The key used for event delegation.
|
||||
fn event_delegation_key(&self) -> Cow<'static, str>;
|
||||
fn event_delegation_key(&self) -> Oco<'static, str>;
|
||||
|
||||
/// Return the options for this type. This is only used when you create a [`Custom`] event
|
||||
/// handler.
|
||||
|
@ -39,12 +40,12 @@ impl<Ev: EventDescriptor> EventDescriptor for undelegated<Ev> {
|
|||
type EventType = Ev::EventType;
|
||||
|
||||
#[inline(always)]
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||
fn event_delegation_key(&self) -> Oco<'static, str> {
|
||||
self.0.event_delegation_key()
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,7 @@ impl<Ev: EventDescriptor> EventDescriptor for undelegated<Ev> {
|
|||
/// A custom event.
|
||||
#[derive(Debug)]
|
||||
pub struct Custom<E: FromWasmAbi = web_sys::Event> {
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
options: Option<web_sys::AddEventListenerOptions>,
|
||||
_event_type: PhantomData<E>,
|
||||
}
|
||||
|
@ -72,11 +73,11 @@ impl<E: FromWasmAbi> Clone for Custom<E> {
|
|||
impl<E: FromWasmAbi> EventDescriptor for Custom<E> {
|
||||
type EventType = E;
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||
fn event_delegation_key(&self) -> Oco<'static, str> {
|
||||
format!("$$${}", self.name).into()
|
||||
}
|
||||
|
||||
|
@ -92,7 +93,7 @@ impl<E: FromWasmAbi> Custom<E> {
|
|||
/// Creates a custom event type that can be used within
|
||||
/// [`HtmlElement::on`](crate::HtmlElement::on), for events
|
||||
/// which are not covered in the [`ev`](crate::ev) module.
|
||||
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
|
||||
pub fn new(name: impl Into<Oco<'static, str>>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
options: None,
|
||||
|
@ -299,12 +300,12 @@ macro_rules! generate_event_types {
|
|||
type EventType = web_sys::$web_event;
|
||||
|
||||
#[inline(always)]
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
stringify!([< $($event)+ >]).into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn event_delegation_key(&self) -> Cow<'static, str> {
|
||||
fn event_delegation_key(&self) -> Oco<'static, str> {
|
||||
concat!("$$$", stringify!([< $($event)+ >])).into()
|
||||
}
|
||||
|
||||
|
|
|
@ -66,12 +66,13 @@ use crate::{
|
|||
macro_helpers::{IntoAttribute, IntoClass, IntoProperty, IntoStyle},
|
||||
Element, Fragment, IntoView, NodeRef, Text, View,
|
||||
};
|
||||
use std::{borrow::Cow, fmt};
|
||||
use leptos_reactive::Oco;
|
||||
use std::fmt;
|
||||
|
||||
/// Trait which allows creating an element tag.
|
||||
pub trait ElementDescriptor: ElementDescriptorBounds {
|
||||
/// The name of the element, i.e., `div`, `p`, `custom-element`.
|
||||
fn name(&self) -> Cow<'static, str>;
|
||||
fn name(&self) -> Oco<'static, str>;
|
||||
|
||||
/// Determines if the tag is void, i.e., `<input>` and `<br>`.
|
||||
#[inline(always)]
|
||||
|
@ -126,7 +127,7 @@ where
|
|||
/// Represents potentially any element.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyElement {
|
||||
pub(crate) name: Cow<'static, str>,
|
||||
pub(crate) name: Oco<'static, str>,
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
pub(crate) element: web_sys::HtmlElement,
|
||||
pub(crate) is_void: bool,
|
||||
|
@ -159,7 +160,7 @@ impl std::convert::AsRef<web_sys::HtmlElement> for AnyElement {
|
|||
}
|
||||
|
||||
impl ElementDescriptor for AnyElement {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
|
@ -178,7 +179,7 @@ impl ElementDescriptor for AnyElement {
|
|||
/// Represents a custom HTML element, such as `<my-element>`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Custom {
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
element: web_sys::HtmlElement,
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
|
@ -187,7 +188,7 @@ pub struct Custom {
|
|||
|
||||
impl Custom {
|
||||
/// Creates a new custom element with the given tag name.
|
||||
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
|
||||
pub fn new(name: impl Into<Oco<'static, str>>) -> Self {
|
||||
let name = name.into();
|
||||
let id = HydrationCtx::id();
|
||||
|
||||
|
@ -266,7 +267,7 @@ impl std::convert::AsRef<web_sys::HtmlElement> for Custom {
|
|||
}
|
||||
|
||||
impl ElementDescriptor for Custom {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
|
@ -295,7 +296,7 @@ cfg_if! {
|
|||
#[educe(Debug)]
|
||||
pub struct HtmlElement<El: ElementDescriptor> {
|
||||
pub(crate) element: El,
|
||||
pub(crate) attrs: SmallVec<[(Cow<'static, str>, Cow<'static, str>); 4]>,
|
||||
pub(crate) attrs: SmallVec<[(Oco<'static, str>, Oco<'static, str>); 4]>,
|
||||
#[educe(Debug(ignore))]
|
||||
pub(crate) children: ElementChildren,
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -308,14 +309,14 @@ cfg_if! {
|
|||
#[educe(Default)]
|
||||
Empty,
|
||||
Children(Vec<View>),
|
||||
InnerHtml(Cow<'static, str>),
|
||||
InnerHtml(Oco<'static, str>),
|
||||
Chunks(Vec<StringOrView>)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone)]
|
||||
pub enum StringOrView {
|
||||
String(Cow<'static, str>),
|
||||
String(Oco<'static, str>),
|
||||
View(std::rc::Rc<dyn Fn() -> View>)
|
||||
}
|
||||
|
||||
|
@ -445,7 +446,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
/// Adds an `id` to the element.
|
||||
#[track_caller]
|
||||
#[inline(always)]
|
||||
pub fn id(self, id: impl Into<Cow<'static, str>>) -> Self {
|
||||
pub fn id(self, id: impl Into<Oco<'static, str>>) -> Self {
|
||||
let id = id.into();
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -575,7 +576,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
#[cfg_attr(all(target_arch = "wasm32", feature = "web"), inline(always))]
|
||||
pub fn attr(
|
||||
self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
name: impl Into<Oco<'static, str>>,
|
||||
attr: impl IntoAttribute,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
|
@ -634,7 +635,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
#[track_caller]
|
||||
pub fn class(
|
||||
self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
name: impl Into<Oco<'static, str>>,
|
||||
class: impl IntoClass,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
|
@ -686,7 +687,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
/// Adds a list of classes separated by ASCII whitespace to an element.
|
||||
#[track_caller]
|
||||
#[inline(always)]
|
||||
pub fn classes(self, classes: impl Into<Cow<'static, str>>) -> Self {
|
||||
pub fn classes(self, classes: impl Into<Oco<'static, str>>) -> Self {
|
||||
self.classes_inner(&classes.into())
|
||||
}
|
||||
|
||||
|
@ -698,7 +699,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = C>,
|
||||
C: Into<Cow<'static, str>>,
|
||||
C: Into<Oco<'static, str>>,
|
||||
{
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
{
|
||||
|
@ -708,12 +709,12 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
|
||||
leptos_reactive::create_effect(
|
||||
move |prev_classes: Option<
|
||||
SmallVec<[Cow<'static, str>; 4]>,
|
||||
SmallVec<[Oco<'static, str>; 4]>,
|
||||
>| {
|
||||
let classes = classes_signal()
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<SmallVec<[Cow<'static, str>; 4]>>(
|
||||
.collect::<SmallVec<[Oco<'static, str>; 4]>>(
|
||||
);
|
||||
|
||||
let new_classes = classes
|
||||
|
@ -797,7 +798,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
#[track_caller]
|
||||
pub fn style(
|
||||
self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
name: impl Into<Oco<'static, str>>,
|
||||
style: impl IntoStyle,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
|
@ -856,7 +857,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
#[track_caller]
|
||||
pub fn prop(
|
||||
self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
name: impl Into<Oco<'static, str>>,
|
||||
value: impl IntoProperty,
|
||||
) -> Self {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -1016,7 +1017,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
|
|||
/// sanitize the input to avoid a cross-site scripting (XSS)
|
||||
/// vulnerability.
|
||||
#[inline(always)]
|
||||
pub fn inner_html(self, html: impl Into<Cow<'static, str>>) -> Self {
|
||||
pub fn inner_html(self, html: impl Into<Oco<'static, str>>) -> Self {
|
||||
let html = html.into();
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
|
@ -1103,7 +1104,7 @@ pub fn custom<El: ElementDescriptor>(el: El) -> HtmlElement<Custom> {
|
|||
|
||||
/// Creates a text node.
|
||||
#[inline(always)]
|
||||
pub fn text(text: impl Into<Cow<'static, str>>) -> Text {
|
||||
pub fn text(text: impl Into<Oco<'static, str>>) -> Text {
|
||||
Text::new(text.into())
|
||||
}
|
||||
|
||||
|
@ -1190,7 +1191,7 @@ macro_rules! generate_html_tags {
|
|||
|
||||
impl ElementDescriptor for [<$tag:camel $($trailing_)?>] {
|
||||
#[inline(always)]
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
stringify!($tag).into()
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ pub use events::{typed as ev, typed::EventHandler};
|
|||
pub use html::HtmlElement;
|
||||
use html::{AnyElement, ElementDescriptor};
|
||||
pub use hydration::{HydrationCtx, HydrationKey};
|
||||
use leptos_reactive::Oco;
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use leptos_reactive::{
|
||||
MaybeProp, MaybeSignal, Memo, ReadSignal, RwSignal, Signal, SignalGet,
|
||||
|
@ -240,7 +241,7 @@ cfg_if! {
|
|||
pub struct Element {
|
||||
#[doc(hidden)]
|
||||
#[cfg(debug_assertions)]
|
||||
pub name: Cow<'static, str>,
|
||||
pub name: Oco<'static, str>,
|
||||
#[doc(hidden)]
|
||||
pub element: web_sys::HtmlElement,
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -261,9 +262,9 @@ cfg_if! {
|
|||
/// HTML element.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Element {
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
is_void: bool,
|
||||
attrs: SmallVec<[(Cow<'static, str>, Cow<'static, str>); 4]>,
|
||||
attrs: SmallVec<[(Oco<'static, str>, Oco<'static, str>); 4]>,
|
||||
children: ElementChildren,
|
||||
id: HydrationKey,
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -396,13 +397,13 @@ impl Element {
|
|||
struct Comment {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
node: web_sys::Node,
|
||||
content: Cow<'static, str>,
|
||||
content: Oco<'static, str>,
|
||||
}
|
||||
|
||||
impl Comment {
|
||||
#[inline]
|
||||
fn new(
|
||||
content: impl Into<Cow<'static, str>>,
|
||||
content: impl Into<Oco<'static, str>>,
|
||||
id: &HydrationKey,
|
||||
closing: bool,
|
||||
) -> Self {
|
||||
|
@ -410,7 +411,7 @@ impl Comment {
|
|||
}
|
||||
|
||||
fn new_inner(
|
||||
content: Cow<'static, str>,
|
||||
content: Oco<'static, str>,
|
||||
id: &HydrationKey,
|
||||
closing: bool,
|
||||
) -> Self {
|
||||
|
@ -466,12 +467,13 @@ pub struct Text {
|
|||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
node: web_sys::Node,
|
||||
/// The current contents of the text node.
|
||||
pub content: Cow<'static, str>,
|
||||
pub content: Oco<'static, str>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Text {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\"{}\"", self.content)
|
||||
fmt::Debug::fmt(&self.content, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,7 +486,7 @@ impl IntoView for Text {
|
|||
|
||||
impl Text {
|
||||
/// Creates a new [`Text`].
|
||||
pub fn new(content: Cow<'static, str>) -> Self {
|
||||
pub fn new(content: Oco<'static, str>) -> Self {
|
||||
Self {
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
node: crate::document()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use leptos_reactive::Oco;
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use leptos_reactive::{
|
||||
MaybeProp, MaybeSignal, Memo, ReadSignal, RwSignal, Signal, SignalGet,
|
||||
|
@ -14,11 +15,11 @@ use wasm_bindgen::UnwrapThrowExt;
|
|||
#[derive(Clone)]
|
||||
pub enum Attribute {
|
||||
/// A plain string value.
|
||||
String(Cow<'static, str>),
|
||||
String(Oco<'static, str>),
|
||||
/// A (presumably reactive) function, which will be run inside an effect to do targeted updates to the attribute.
|
||||
Fn(Rc<dyn Fn() -> Attribute>),
|
||||
/// An optional string value, which sets the attribute to the value if `Some` and removes the attribute if `None`.
|
||||
Option(Option<Cow<'static, str>>),
|
||||
Option(Option<Oco<'static, str>>),
|
||||
/// A boolean attribute, which sets the attribute if `true` and removes the attribute if `false`.
|
||||
Bool(bool),
|
||||
}
|
||||
|
@ -29,7 +30,7 @@ impl Attribute {
|
|||
pub fn as_value_string(
|
||||
&self,
|
||||
attr_name: &'static str,
|
||||
) -> Cow<'static, str> {
|
||||
) -> Oco<'static, str> {
|
||||
match self {
|
||||
Attribute::String(value) => {
|
||||
format!("{attr_name}=\"{value}\"").into()
|
||||
|
@ -46,14 +47,14 @@ impl Attribute {
|
|||
.map(|value| format!("{attr_name}=\"{value}\"").into())
|
||||
.unwrap_or_default(),
|
||||
Attribute::Bool(include) => {
|
||||
Cow::Borrowed(if *include { attr_name } else { "" })
|
||||
Oco::Borrowed(if *include { attr_name } else { "" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the attribute to its HTML value at that moment, not including
|
||||
/// the attribute name, so it can be rendered on the server.
|
||||
pub fn as_nameless_value_string(&self) -> Option<Cow<'static, str>> {
|
||||
pub fn as_nameless_value_string(&self) -> Option<Oco<'static, str>> {
|
||||
match self {
|
||||
Attribute::String(value) => Some(value.clone()),
|
||||
Attribute::Fn(f) => {
|
||||
|
@ -148,7 +149,7 @@ impl IntoAttribute for Option<Attribute> {
|
|||
impl IntoAttribute for String {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::String(Cow::Owned(self))
|
||||
Attribute::String(Oco::Owned(self))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
|
@ -157,13 +158,22 @@ impl IntoAttribute for String {
|
|||
impl IntoAttribute for &'static str {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::String(Cow::Borrowed(self))
|
||||
Attribute::String(Oco::Borrowed(self))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Cow<'static, str> {
|
||||
impl IntoAttribute for Rc<str> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::String(Oco::Counted(self))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Oco<'static, str> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::String(self)
|
||||
|
@ -184,7 +194,7 @@ impl IntoAttribute for bool {
|
|||
impl IntoAttribute for Option<String> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::Option(self.map(Cow::Owned))
|
||||
Attribute::Option(self.map(Oco::Owned))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
|
@ -193,13 +203,31 @@ impl IntoAttribute for Option<String> {
|
|||
impl IntoAttribute for Option<&'static str> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::Option(self.map(Cow::Borrowed))
|
||||
Attribute::Option(self.map(Oco::Borrowed))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Option<Rc<str>> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::Option(self.map(Oco::Counted))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Option<Cow<'static, str>> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::Option(self.map(Oco::from))
|
||||
}
|
||||
|
||||
impl_into_attr_boxed! {}
|
||||
}
|
||||
|
||||
impl IntoAttribute for Option<Oco<'static, str>> {
|
||||
#[inline(always)]
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::Option(self)
|
||||
|
@ -331,7 +359,7 @@ attr_signal_type_optional!(MaybeProp<T>);
|
|||
#[inline(never)]
|
||||
pub fn attribute_helper(
|
||||
el: &web_sys::Element,
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
value: Attribute,
|
||||
) {
|
||||
use leptos_reactive::create_render_effect;
|
||||
|
|
|
@ -65,14 +65,14 @@ impl Class {
|
|||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use std::borrow::Cow;
|
||||
use leptos_reactive::Oco;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
#[doc(hidden)]
|
||||
#[inline(never)]
|
||||
pub fn class_helper(
|
||||
el: &web_sys::Element,
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
value: Class,
|
||||
) {
|
||||
use leptos_reactive::create_render_effect;
|
||||
|
|
|
@ -115,13 +115,13 @@ prop_signal_type!(MaybeSignal<T>);
|
|||
prop_signal_type_optional!(MaybeProp<T>);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use std::borrow::Cow;
|
||||
use leptos_reactive::Oco;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
#[inline(never)]
|
||||
pub(crate) fn property_helper(
|
||||
el: &web_sys::Element,
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
value: Property,
|
||||
) {
|
||||
use leptos_reactive::create_render_effect;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use leptos_reactive::Oco;
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use leptos_reactive::{
|
||||
MaybeProp, MaybeSignal, Memo, ReadSignal, RwSignal, Signal, SignalGet,
|
||||
|
@ -8,9 +9,9 @@ use std::{borrow::Cow, rc::Rc};
|
|||
#[derive(Clone)]
|
||||
pub enum Style {
|
||||
/// A plain string value.
|
||||
Value(Cow<'static, str>),
|
||||
Value(Oco<'static, str>),
|
||||
/// An optional string value, which sets the property to the value if `Some` and removes the property if `None`.
|
||||
Option(Option<Cow<'static, str>>),
|
||||
Option(Option<Oco<'static, str>>),
|
||||
/// A (presumably reactive) function, which will be run inside an effect to update the style.
|
||||
Fn(Rc<dyn Fn() -> Style>),
|
||||
}
|
||||
|
@ -45,28 +46,70 @@ pub trait IntoStyle {
|
|||
impl IntoStyle for &'static str {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Value(self.into())
|
||||
Style::Value(Oco::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for String {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Value(Oco::Owned(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Rc<str> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Value(Oco::Counted(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Cow<'static, str> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Value(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Oco<'static, str> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Option<&'static str> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Option(self.map(Cow::Borrowed))
|
||||
Style::Option(self.map(Oco::Borrowed))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Option<String> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Option(self.map(Cow::Owned))
|
||||
Style::Option(self.map(Oco::Owned))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Option<Rc<str>> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Option(self.map(Oco::Counted))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Option<Cow<'static, str>> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Option(self.map(Oco::from))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Option<Oco<'static, str>> {
|
||||
#[inline(always)]
|
||||
fn into_style(self) -> Style {
|
||||
Style::Option(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,7 +130,7 @@ impl Style {
|
|||
pub fn as_value_string(
|
||||
&self,
|
||||
style_name: &'static str,
|
||||
) -> Option<Cow<'static, str>> {
|
||||
) -> Option<Oco<'static, str>> {
|
||||
match self {
|
||||
Style::Value(value) => {
|
||||
Some(format!("{style_name}: {value};").into())
|
||||
|
@ -111,10 +154,11 @@ impl Style {
|
|||
#[inline(never)]
|
||||
pub fn style_helper(
|
||||
el: &web_sys::Element,
|
||||
name: Cow<'static, str>,
|
||||
name: Oco<'static, str>,
|
||||
value: Style,
|
||||
) {
|
||||
use leptos_reactive::create_render_effect;
|
||||
use std::ops::Deref;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
let el = el.unchecked_ref::<web_sys::HtmlElement>();
|
||||
|
@ -132,16 +176,16 @@ pub fn style_helper(
|
|||
_ => unreachable!(),
|
||||
};
|
||||
if old.as_ref() != Some(&new) {
|
||||
style_expression(&style_list, &name, new.as_ref(), true)
|
||||
style_expression(&style_list, &name, new.as_deref(), true)
|
||||
}
|
||||
new
|
||||
});
|
||||
}
|
||||
Style::Value(value) => {
|
||||
style_expression(&style_list, &name, Some(&value), false)
|
||||
style_expression(&style_list, &name, Some(value.deref()), false)
|
||||
}
|
||||
Style::Option(value) => {
|
||||
style_expression(&style_list, &name, value.as_ref(), false)
|
||||
style_expression(&style_list, &name, value.as_deref(), false)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -151,7 +195,7 @@ pub fn style_helper(
|
|||
pub(crate) fn style_expression(
|
||||
style_list: &web_sys::CssStyleDeclaration,
|
||||
style_name: &str,
|
||||
value: Option<&Cow<'static, str>>,
|
||||
value: Option<&str>,
|
||||
force: bool,
|
||||
) {
|
||||
use crate::HydrationCtx;
|
||||
|
@ -160,7 +204,7 @@ pub(crate) fn style_expression(
|
|||
let style_name = wasm_bindgen::intern(style_name);
|
||||
|
||||
if let Some(value) = value {
|
||||
if let Err(e) = style_list.set_property(style_name, &value) {
|
||||
if let Err(e) = style_list.set_property(style_name, value) {
|
||||
crate::error!("[HtmlElement::style()] {e:?}");
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use super::{ElementDescriptor, HtmlElement};
|
||||
use crate::HydrationCtx;
|
||||
use cfg_if::cfg_if;
|
||||
use std::borrow::Cow;
|
||||
use leptos_reactive::Oco;
|
||||
cfg_if! {
|
||||
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
|
||||
use once_cell::unsync::Lazy as LazyCell;
|
||||
|
@ -145,7 +145,7 @@ macro_rules! generate_math_tags {
|
|||
}
|
||||
|
||||
impl ElementDescriptor for [<$tag:camel $($second:camel $($third:camel)?)?>] {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
stringify!($tag).into()
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ use crate::{
|
|||
use cfg_if::cfg_if;
|
||||
use futures::{stream::FuturesUnordered, Future, Stream, StreamExt};
|
||||
use itertools::Itertools;
|
||||
use leptos_reactive::*;
|
||||
use std::{borrow::Cow, pin::Pin};
|
||||
use leptos_reactive::{Oco, *};
|
||||
use std::pin::Pin;
|
||||
|
||||
type PinnedFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
||||
|
||||
|
@ -30,7 +30,7 @@ type PinnedFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
|||
any(debug_assertions, feature = "ssr"),
|
||||
instrument(level = "info", skip_all,)
|
||||
)]
|
||||
pub fn render_to_string<F, N>(f: F) -> String
|
||||
pub fn render_to_string<F, N>(f: F) -> Oco<'static, str>
|
||||
where
|
||||
F: FnOnce() -> N + 'static,
|
||||
N: IntoView,
|
||||
|
@ -42,7 +42,7 @@ where
|
|||
|
||||
runtime.dispose();
|
||||
|
||||
html.into()
|
||||
html
|
||||
}
|
||||
|
||||
/// Renders a function to a stream of HTML strings.
|
||||
|
@ -87,7 +87,7 @@ pub fn render_to_stream(
|
|||
)]
|
||||
pub fn render_to_stream_with_prefix(
|
||||
view: impl FnOnce() -> View + 'static,
|
||||
prefix: impl FnOnce() -> Cow<'static, str> + 'static,
|
||||
prefix: impl FnOnce() -> Oco<'static, str> + 'static,
|
||||
) -> impl Stream<Item = String> {
|
||||
let (stream, runtime) =
|
||||
render_to_stream_with_prefix_undisposed(view, prefix);
|
||||
|
@ -116,7 +116,7 @@ pub fn render_to_stream_with_prefix(
|
|||
)]
|
||||
pub fn render_to_stream_with_prefix_undisposed(
|
||||
view: impl FnOnce() -> View + 'static,
|
||||
prefix: impl FnOnce() -> Cow<'static, str> + 'static,
|
||||
prefix: impl FnOnce() -> Oco<'static, str> + 'static,
|
||||
) -> (impl Stream<Item = String>, RuntimeId) {
|
||||
render_to_stream_with_prefix_undisposed_with_context(view, prefix, || {})
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ pub fn render_to_stream_with_prefix_undisposed(
|
|||
)]
|
||||
pub fn render_to_stream_with_prefix_undisposed_with_context(
|
||||
view: impl FnOnce() -> View + 'static,
|
||||
prefix: impl FnOnce() -> Cow<'static, str> + 'static,
|
||||
prefix: impl FnOnce() -> Oco<'static, str> + 'static,
|
||||
additional_context: impl FnOnce() + 'static,
|
||||
) -> (impl Stream<Item = String>, RuntimeId) {
|
||||
render_to_stream_with_prefix_undisposed_with_context_and_block_replacement(
|
||||
|
@ -179,7 +179,7 @@ pub fn render_to_stream_with_prefix_undisposed_with_context(
|
|||
)]
|
||||
pub fn render_to_stream_with_prefix_undisposed_with_context_and_block_replacement(
|
||||
view: impl FnOnce() -> View + 'static,
|
||||
prefix: impl FnOnce() -> Cow<'static, str> + 'static,
|
||||
prefix: impl FnOnce() -> Oco<'static, str> + 'static,
|
||||
additional_context: impl FnOnce() + 'static,
|
||||
replace_blocks: bool,
|
||||
) -> (impl Stream<Item = String>, RuntimeId) {
|
||||
|
@ -363,7 +363,7 @@ impl View {
|
|||
any(debug_assertions, feature = "ssr"),
|
||||
instrument(level = "info", skip_all,)
|
||||
)]
|
||||
pub fn render_to_string(self) -> Cow<'static, str> {
|
||||
pub fn render_to_string(self) -> Oco<'static, str> {
|
||||
#[cfg(all(feature = "web", feature = "ssr"))]
|
||||
crate::console_error(
|
||||
"\n[DANGER] You have both `csr` and `ssr` or `hydrate` and `ssr` \
|
||||
|
@ -381,7 +381,7 @@ impl View {
|
|||
pub(crate) fn render_to_string_helper(
|
||||
self,
|
||||
dont_escape_text: bool,
|
||||
) -> Cow<'static, str> {
|
||||
) -> Oco<'static, str> {
|
||||
match self {
|
||||
View::Text(node) => {
|
||||
if dont_escape_text {
|
||||
|
@ -450,7 +450,7 @@ impl View {
|
|||
)
|
||||
.into()
|
||||
})
|
||||
as Box<dyn FnOnce() -> Cow<'static, str>>,
|
||||
as Box<dyn FnOnce() -> Oco<'static, str>>,
|
||||
),
|
||||
CoreComponent::DynChild(node) => {
|
||||
let child = node.child.take();
|
||||
|
@ -500,7 +500,7 @@ impl View {
|
|||
"".into()
|
||||
}
|
||||
})
|
||||
as Box<dyn FnOnce() -> Cow<'static, str>>,
|
||||
as Box<dyn FnOnce() -> Oco<'static, str>>,
|
||||
)
|
||||
}
|
||||
CoreComponent::Each(node) => {
|
||||
|
@ -554,7 +554,7 @@ impl View {
|
|||
.join("")
|
||||
.into()
|
||||
})
|
||||
as Box<dyn FnOnce() -> Cow<'static, str>>,
|
||||
as Box<dyn FnOnce() -> Oco<'static, str>>,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -598,15 +598,15 @@ impl View {
|
|||
.join("")
|
||||
.into()
|
||||
} else {
|
||||
let tag_name = el.name;
|
||||
let tag_name: Oco<'_, str> = el.name;
|
||||
|
||||
let mut inner_html = None;
|
||||
let mut inner_html: Option<Oco<'_, str>> = None;
|
||||
|
||||
let attrs = el
|
||||
.attrs
|
||||
.into_iter()
|
||||
.filter_map(
|
||||
|(name, value)| -> Option<Cow<'static, str>> {
|
||||
|(name, value)| -> Option<Oco<'static, str>> {
|
||||
if value.is_empty() {
|
||||
Some(format!(" {name}").into())
|
||||
} else if name == "inner_html" {
|
||||
|
@ -729,9 +729,9 @@ pub(crate) fn render_serializers(
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn escape_attr<T>(value: &T) -> Cow<'_, str>
|
||||
pub fn escape_attr<T>(value: &T) -> Oco<'_, str>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
html_escape::encode_double_quoted_attribute(value)
|
||||
html_escape::encode_double_quoted_attribute(value).into()
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use cfg_if::cfg_if;
|
|||
use futures::{channel::mpsc::UnboundedSender, Stream, StreamExt};
|
||||
use itertools::Itertools;
|
||||
use leptos_reactive::{
|
||||
create_runtime, suspense::StreamChunk, RuntimeId, SharedContext,
|
||||
create_runtime, suspense::StreamChunk, Oco, RuntimeId, SharedContext,
|
||||
};
|
||||
use std::{borrow::Cow, collections::VecDeque};
|
||||
|
||||
|
@ -59,7 +59,7 @@ pub fn render_to_stream_in_order(
|
|||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub fn render_to_stream_in_order_with_prefix(
|
||||
view: impl FnOnce() -> View + 'static,
|
||||
prefix: impl FnOnce() -> Cow<'static, str> + 'static,
|
||||
prefix: impl FnOnce() -> Oco<'static, str> + 'static,
|
||||
) -> impl Stream<Item = String> {
|
||||
#[cfg(all(feature = "web", feature = "ssr"))]
|
||||
crate::console_error(
|
||||
|
@ -89,7 +89,7 @@ pub fn render_to_stream_in_order_with_prefix(
|
|||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub fn render_to_stream_in_order_with_prefix_undisposed_with_context(
|
||||
view: impl FnOnce() -> View + 'static,
|
||||
prefix: impl FnOnce() -> Cow<'static, str> + 'static,
|
||||
prefix: impl FnOnce() -> Oco<'static, str> + 'static,
|
||||
additional_context: impl FnOnce() + 'static,
|
||||
) -> (impl Stream<Item = String>, RuntimeId) {
|
||||
HydrationCtx::reset_id();
|
||||
|
@ -287,12 +287,11 @@ impl View {
|
|||
StringOrView::String(string) => {
|
||||
chunks.push_back(StreamChunk::Sync(string))
|
||||
}
|
||||
StringOrView::View(view) => {
|
||||
view().into_stream_chunks_helper(
|
||||
StringOrView::View(view) => view()
|
||||
.into_stream_chunks_helper(
|
||||
chunks,
|
||||
is_script_or_style,
|
||||
);
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -350,7 +349,7 @@ impl View {
|
|||
}
|
||||
}
|
||||
ElementChildren::InnerHtml(inner_html) => {
|
||||
chunks.push_back(StreamChunk::Sync(inner_html));
|
||||
chunks.push_back(StreamChunk::Sync(inner_html))
|
||||
}
|
||||
// handled above
|
||||
ElementChildren::Chunks(_) => unreachable!(),
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
use super::{html::HTML_ELEMENT_DEREF_UNIMPLEMENTED_MSG, HydrationKey};
|
||||
use super::{ElementDescriptor, HtmlElement};
|
||||
use crate::HydrationCtx;
|
||||
use leptos_reactive::Oco;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use once_cell::unsync::Lazy as LazyCell;
|
||||
use std::borrow::Cow;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
|
@ -142,7 +142,7 @@ macro_rules! generate_svg_tags {
|
|||
}
|
||||
|
||||
impl ElementDescriptor for [<$tag:camel $($second:camel $($third:camel)?)?>] {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> Oco<'static, str> {
|
||||
stringify!($tag).into()
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ mod effect;
|
|||
mod hydration;
|
||||
mod memo;
|
||||
mod node;
|
||||
pub mod oco;
|
||||
mod resource;
|
||||
mod runtime;
|
||||
mod selector;
|
||||
|
@ -107,6 +108,7 @@ pub use effect::*;
|
|||
pub use hydration::{FragmentData, SharedContext};
|
||||
pub use memo::*;
|
||||
pub use node::Disposer;
|
||||
pub use oco::*;
|
||||
pub use resource::*;
|
||||
use runtime::*;
|
||||
pub use runtime::{
|
||||
|
|
681
leptos_reactive/src/oco.rs
Normal file
681
leptos_reactive/src/oco.rs
Normal file
|
@ -0,0 +1,681 @@
|
|||
//! This module contains the `Oco` (Owned Clones Once) smart pointer,
|
||||
//! which is used to store immutable references to values.
|
||||
//! This is useful for storing, for example, strings.
|
||||
|
||||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
ffi::{CStr, OsStr},
|
||||
fmt,
|
||||
hash::Hash,
|
||||
ops::{Add, Deref},
|
||||
path::Path,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
/// "Owned Clones Once" - a smart pointer that can be either a reference,
|
||||
/// an owned value, or a reference counted pointer. This is useful for
|
||||
/// storing immutable values, such as strings, in a way that is cheap to
|
||||
/// clone and pass around.
|
||||
///
|
||||
/// The `Clone` implementation is amortized `O(1)`. Cloning the [`Oco::Borrowed`]
|
||||
/// variant simply copies the references (`O(1)`). Cloning the [`Oco::Counted`]
|
||||
/// variant increments a reference count (`O(1)`). Cloning the [`Oco::Owned`]
|
||||
/// variant upgrades it to [`Oco::Counted`], which requires an `O(n)` clone of the
|
||||
/// data, but all subsequent clones will be `O(1)`.
|
||||
pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
|
||||
/// A static reference to a value.
|
||||
Borrowed(&'a T),
|
||||
/// A reference counted pointer to a value.
|
||||
Counted(Rc<T>),
|
||||
/// An owned value.
|
||||
Owned(<T as ToOwned>::Owned),
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToOwned> Oco<'_, T> {
|
||||
/// Converts the value into an owned value.
|
||||
pub fn into_owned(self) -> <T as ToOwned>::Owned {
|
||||
match self {
|
||||
Oco::Borrowed(v) => v.to_owned(),
|
||||
Oco::Counted(v) => v.as_ref().to_owned(),
|
||||
Oco::Owned(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the value is [`Oco::Borrowed`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::rc::Rc;
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// assert!(Oco::<str>::Borrowed("Hello").is_borrowed());
|
||||
/// assert!(!Oco::<str>::Counted(Rc::from("Hello")).is_borrowed());
|
||||
/// assert!(!Oco::<str>::Owned("Hello".to_string()).is_borrowed());
|
||||
/// ```
|
||||
pub const fn is_borrowed(&self) -> bool {
|
||||
matches!(self, Oco::Borrowed(_))
|
||||
}
|
||||
|
||||
/// Checks if the value is [`Oco::Counted`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::rc::Rc;
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// assert!(Oco::<str>::Counted(Rc::from("Hello")).is_counted());
|
||||
/// assert!(!Oco::<str>::Borrowed("Hello").is_counted());
|
||||
/// assert!(!Oco::<str>::Owned("Hello".to_string()).is_counted());
|
||||
/// ```
|
||||
pub const fn is_counted(&self) -> bool {
|
||||
matches!(self, Oco::Counted(_))
|
||||
}
|
||||
|
||||
/// Checks if the value is [`Oco::Owned`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::rc::Rc;
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// assert!(Oco::<str>::Owned("Hello".to_string()).is_owned());
|
||||
/// assert!(!Oco::<str>::Borrowed("Hello").is_owned());
|
||||
/// assert!(!Oco::<str>::Counted(Rc::from("Hello")).is_owned());
|
||||
/// ```
|
||||
pub const fn is_owned(&self) -> bool {
|
||||
matches!(self, Oco::Owned(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToOwned> Deref for Oco<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
match self {
|
||||
Oco::Borrowed(v) => v,
|
||||
Oco::Owned(v) => v.borrow(),
|
||||
Oco::Counted(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToOwned> Borrow<T> for Oco<'_, T> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &T {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToOwned> AsRef<T> for Oco<'_, T> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &T {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Oco<'_, str> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Oco<'_, OsStr> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_os_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
// pub fn as_{slice}(&self) -> &{slice}
|
||||
// --------------------------------------
|
||||
|
||||
impl Oco<'_, str> {
|
||||
/// Returns a `&str` slice of this [`Oco`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// let oco = Oco::<str>::Borrowed("Hello");
|
||||
/// let s: &str = oco.as_str();
|
||||
/// assert_eq!(s, "Hello");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Oco<'_, CStr> {
|
||||
/// Returns a `&CStr` slice of this [`Oco`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let oco =
|
||||
/// Oco::<CStr>::Borrowed(CStr::from_bytes_with_nul(b"Hello\0").unwrap());
|
||||
/// let s: &CStr = oco.as_c_str();
|
||||
/// assert_eq!(s, CStr::from_bytes_with_nul(b"Hello\0").unwrap());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_c_str(&self) -> &CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Oco<'_, OsStr> {
|
||||
/// Returns a `&OsStr` slice of this [`Oco`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// use std::ffi::OsStr;
|
||||
///
|
||||
/// let oco = Oco::<OsStr>::Borrowed(OsStr::new("Hello"));
|
||||
/// let s: &OsStr = oco.as_os_str();
|
||||
/// assert_eq!(s, OsStr::new("Hello"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_os_str(&self) -> &OsStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Oco<'_, Path> {
|
||||
/// Returns a `&Path` slice of this [`Oco`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let oco = Oco::<Path>::Borrowed(Path::new("Hello"));
|
||||
/// let s: &Path = oco.as_path();
|
||||
/// assert_eq!(s, Path::new("Hello"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_path(&self) -> &Path {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Oco<'_, [T]>
|
||||
where
|
||||
[T]: ToOwned,
|
||||
{
|
||||
/// Returns a `&[T]` slice of this [`Oco`].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// let oco = Oco::<[u8]>::Borrowed(b"Hello");
|
||||
/// let s: &[u8] = oco.as_slice();
|
||||
/// assert_eq!(s, b"Hello");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
// Cloning (has to be implemented manually because of the `Rc<T>: From<&<T as ToOwned>::Owned>` bound)
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
impl Clone for Oco<'_, str> {
|
||||
/// Returns a new [`Oco`] with the same value as this one.
|
||||
/// If the value is [`Oco::Owned`], this will convert it into
|
||||
/// [`Oco::Counted`], so that the next clone will be O(1).
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// let oco = Oco::<str>::Owned("Hello".to_string());
|
||||
/// let oco2 = oco.clone();
|
||||
/// assert_eq!(oco, oco2);
|
||||
/// assert!(oco2.is_counted());
|
||||
/// ```
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v),
|
||||
Oco::Counted(v) => Oco::Counted(v.clone()),
|
||||
Oco::Owned(v) => Oco::Counted(Rc::<str>::from(v.as_str())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Oco<'_, CStr> {
|
||||
/// Returns a new [`Oco`] with the same value as this one.
|
||||
/// If the value is [`Oco::Owned`], this will convert it into
|
||||
/// [`Oco::Counted`], so that the next clone will be O(1).
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let oco = Oco::<CStr>::Owned(
|
||||
/// CStr::from_bytes_with_nul(b"Hello\0").unwrap().to_owned(),
|
||||
/// );
|
||||
/// let oco2 = oco.clone();
|
||||
/// assert_eq!(oco, oco2);
|
||||
/// assert!(oco2.is_counted());
|
||||
/// ```
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v),
|
||||
Oco::Counted(v) => Oco::Counted(v.clone()),
|
||||
Oco::Owned(v) => Oco::Counted(Rc::<CStr>::from(v.as_c_str())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Oco<'_, OsStr> {
|
||||
/// Returns a new [`Oco`] with the same value as this one.
|
||||
/// If the value is [`Oco::Owned`], this will convert it into
|
||||
/// [`Oco::Counted`], so that the next clone will be O(1).
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// use std::ffi::OsStr;
|
||||
///
|
||||
/// let oco = Oco::<OsStr>::Owned(OsStr::new("Hello").to_owned());
|
||||
/// let oco2 = oco.clone();
|
||||
/// assert_eq!(oco, oco2);
|
||||
/// assert!(oco2.is_counted());
|
||||
/// ```
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v),
|
||||
Oco::Counted(v) => Oco::Counted(v.clone()),
|
||||
Oco::Owned(v) => Oco::Counted(Rc::<OsStr>::from(v.as_os_str())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Oco<'_, Path> {
|
||||
/// Returns a new [`Oco`] with the same value as this one.
|
||||
/// If the value is [`Oco::Owned`], this will convert it into
|
||||
/// [`Oco::Counted`], so that the next clone will be O(1).
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// use std::path::Path;
|
||||
///
|
||||
/// let oco = Oco::<Path>::Owned(Path::new("Hello").to_owned());
|
||||
/// let oco2 = oco.clone();
|
||||
/// assert_eq!(oco, oco2);
|
||||
/// assert!(oco2.is_counted());
|
||||
/// ```
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v),
|
||||
Oco::Counted(v) => Oco::Counted(v.clone()),
|
||||
Oco::Owned(v) => Oco::Counted(Rc::<Path>::from(v.as_path())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for Oco<'_, [T]>
|
||||
where
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
{
|
||||
/// Returns a new [`Oco`] with the same value as this one.
|
||||
/// If the value is [`Oco::Owned`], this will convert it into
|
||||
/// [`Oco::Counted`], so that the next clone will be O(1).
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use leptos_reactive::oco::Oco;
|
||||
/// let oco = Oco::<[i32]>::Owned(vec![1, 2, 3]);
|
||||
/// let oco2 = oco.clone();
|
||||
/// assert_eq!(oco, oco2);
|
||||
/// assert!(oco2.is_counted());
|
||||
/// ```
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v),
|
||||
Oco::Counted(v) => Oco::Counted(v.clone()),
|
||||
Oco::Owned(v) => Oco::Counted(Rc::<[T]>::from(v.as_slice())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Default for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
T::Owned: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Oco::Owned(T::Owned::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, A: ?Sized, B: ?Sized> PartialEq<Oco<'b, B>> for Oco<'a, A>
|
||||
where
|
||||
A: PartialEq<B>,
|
||||
A: ToOwned,
|
||||
B: ToOwned,
|
||||
{
|
||||
fn eq(&self, other: &Oco<'b, B>) -> bool {
|
||||
**self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + ToOwned + Eq> Eq for Oco<'_, T> {}
|
||||
|
||||
impl<'a, 'b, A: ?Sized, B: ?Sized> PartialOrd<Oco<'b, B>> for Oco<'a, A>
|
||||
where
|
||||
A: PartialOrd<B>,
|
||||
A: ToOwned,
|
||||
B: ToOwned,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Oco<'b, B>) -> Option<std::cmp::Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Ord> Ord for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Hash> Hash for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> From<&'a T> for Oco<'a, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn from(v: &'a T) -> Self {
|
||||
Oco::Borrowed(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> From<Cow<'a, T>> for Oco<'a, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn from(v: Cow<'a, T>) -> Self {
|
||||
match v {
|
||||
Cow::Borrowed(v) => Oco::Borrowed(v),
|
||||
Cow::Owned(v) => Oco::Owned(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> From<Oco<'a, T>> for Cow<'a, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn from(value: Oco<'a, T>) -> Self {
|
||||
match value {
|
||||
Oco::Borrowed(v) => Cow::Borrowed(v),
|
||||
Oco::Owned(v) => Cow::Owned(v),
|
||||
Oco::Counted(v) => Cow::Owned(v.as_ref().to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> From<Rc<T>> for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn from(v: Rc<T>) -> Self {
|
||||
Oco::Counted(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> From<Box<T>> for Oco<'_, T>
|
||||
where
|
||||
T: ToOwned,
|
||||
{
|
||||
fn from(v: Box<T>) -> Self {
|
||||
Oco::Counted(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Oco<'_, str> {
|
||||
fn from(v: String) -> Self {
|
||||
Oco::Owned(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Oco<'_, str>> for String {
|
||||
fn from(v: Oco<'_, str>) -> Self {
|
||||
match v {
|
||||
Oco::Borrowed(v) => v.to_owned(),
|
||||
Oco::Counted(v) => v.as_ref().to_owned(),
|
||||
Oco::Owned(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for Oco<'_, [T]>
|
||||
where
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
{
|
||||
fn from(v: Vec<T>) -> Self {
|
||||
Oco::Owned(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> From<&'a [T; N]> for Oco<'a, [T]>
|
||||
where
|
||||
[T]: ToOwned,
|
||||
{
|
||||
fn from(v: &'a [T; N]) -> Self {
|
||||
Oco::Borrowed(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Oco<'a, str>> for Oco<'a, [u8]> {
|
||||
fn from(v: Oco<'a, str>) -> Self {
|
||||
match v {
|
||||
Oco::Borrowed(v) => Oco::Borrowed(v.as_bytes()),
|
||||
Oco::Owned(v) => Oco::Owned(v.into_bytes()),
|
||||
Oco::Counted(v) => Oco::Counted(v.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned from [`Oco::try_from`] for unsuccessful
|
||||
/// conversion from `Oco<'_, [u8]>` to `Oco<'_, str>`.
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
#[error("invalid utf-8 sequence: {_0}")]
|
||||
pub enum FromUtf8Error {
|
||||
/// Error for conversion of [`Oco::Borrowed`] and [`Oco::Counted`] variants
|
||||
/// (`&[u8]` to `&str`).
|
||||
#[error("{_0}")]
|
||||
StrFromBytes(
|
||||
#[source]
|
||||
#[from]
|
||||
std::str::Utf8Error,
|
||||
),
|
||||
/// Error for conversion of [`Oco::Owned`] variant (`Vec<u8>` to `String`).
|
||||
#[error("{_0}")]
|
||||
StringFromBytes(
|
||||
#[source]
|
||||
#[from]
|
||||
std::string::FromUtf8Error,
|
||||
),
|
||||
}
|
||||
|
||||
macro_rules! impl_slice_eq {
|
||||
([$($g:tt)*] $((where $($w:tt)+))?, $lhs:ty, $rhs: ty) => {
|
||||
impl<$($g)*> PartialEq<$rhs> for $lhs
|
||||
$(where
|
||||
$($w)*)?
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
PartialEq::eq(&self[..], &other[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($g)*> PartialEq<$lhs> for $rhs
|
||||
$(where
|
||||
$($w)*)?
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
PartialEq::eq(&self[..], &other[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_slice_eq!([], Oco<'_, str>, str);
|
||||
impl_slice_eq!(['a, 'b], Oco<'a, str>, &'b str);
|
||||
impl_slice_eq!([], Oco<'_, str>, String);
|
||||
impl_slice_eq!(['a, 'b], Oco<'a, str>, Cow<'b, str>);
|
||||
|
||||
impl_slice_eq!([T: PartialEq] (where [T]: ToOwned), Oco<'_, [T]>, [T]);
|
||||
impl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, &'b [T]);
|
||||
impl_slice_eq!([T: PartialEq] (where [T]: ToOwned), Oco<'_, [T]>, Vec<T>);
|
||||
impl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, Cow<'b, [T]>);
|
||||
|
||||
impl<'a, 'b> Add<&'b str> for Oco<'a, str> {
|
||||
type Output = Oco<'static, str>;
|
||||
|
||||
fn add(self, rhs: &'b str) -> Self::Output {
|
||||
Oco::Owned(String::from(self) + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<Cow<'b, str>> for Oco<'a, str> {
|
||||
type Output = Oco<'static, str>;
|
||||
|
||||
fn add(self, rhs: Cow<'b, str>) -> Self::Output {
|
||||
Oco::Owned(String::from(self) + rhs.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<Oco<'b, str>> for Oco<'a, str> {
|
||||
type Output = Oco<'static, str>;
|
||||
|
||||
fn add(self, rhs: Oco<'b, str>) -> Self::Output {
|
||||
Oco::Owned(String::from(self) + rhs.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Oco<'a, str>> for String {
|
||||
fn from_iter<T: IntoIterator<Item = Oco<'a, str>>>(iter: T) -> Self {
|
||||
iter.into_iter().fold(String::new(), |mut acc, item| {
|
||||
acc.push_str(item.as_ref());
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn debug_fmt_should_display_quotes_for_strings() {
|
||||
let s: Oco<str> = Oco::Borrowed("hello");
|
||||
assert_eq!(format!("{:?}", s), "\"hello\"");
|
||||
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
||||
assert_eq!(format!("{:?}", s), "\"hello\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_eq_should_compare_str_to_str() {
|
||||
let s: Oco<str> = Oco::Borrowed("hello");
|
||||
assert_eq!(s, "hello");
|
||||
assert_eq!("hello", s);
|
||||
assert_eq!(s, String::from("hello"));
|
||||
assert_eq!(String::from("hello"), s);
|
||||
assert_eq!(s, Cow::from("hello"));
|
||||
assert_eq!(Cow::from("hello"), s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_eq_should_compare_slice_to_slice() {
|
||||
let s: Oco<[i32]> = Oco::Borrowed([1, 2, 3].as_slice());
|
||||
assert_eq!(s, [1, 2, 3].as_slice());
|
||||
assert_eq!([1, 2, 3].as_slice(), s);
|
||||
assert_eq!(s, vec![1, 2, 3]);
|
||||
assert_eq!(vec![1, 2, 3], s);
|
||||
assert_eq!(s, Cow::<'_, [i32]>::Borrowed(&[1, 2, 3]));
|
||||
assert_eq!(Cow::<'_, [i32]>::Borrowed(&[1, 2, 3]), s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_should_concatenate_strings() {
|
||||
let s: Oco<str> = Oco::Borrowed("hello");
|
||||
assert_eq!(s.clone() + " world", "hello world");
|
||||
assert_eq!(s.clone() + Cow::from(" world"), "hello world");
|
||||
assert_eq!(s + Oco::from(" world"), "hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_str_should_return_a_str() {
|
||||
let s: Oco<str> = Oco::Borrowed("hello");
|
||||
assert_eq!(s.as_str(), "hello");
|
||||
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
||||
assert_eq!(s.as_str(), "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_slice_should_return_a_slice() {
|
||||
let s: Oco<[i32]> = Oco::Borrowed([1, 2, 3].as_slice());
|
||||
assert_eq!(s.as_slice(), [1, 2, 3].as_slice());
|
||||
let s: Oco<[i32]> = Oco::Counted(Rc::from([1, 2, 3]));
|
||||
assert_eq!(s.as_slice(), [1, 2, 3].as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_for_str_should_return_an_empty_string() {
|
||||
let s: Oco<str> = Default::default();
|
||||
assert!(s.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_for_slice_should_return_an_empty_slice() {
|
||||
let s: Oco<[i32]> = Default::default();
|
||||
assert!(s.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_for_any_option_should_return_none() {
|
||||
let s: Oco<Option<i32>> = Default::default();
|
||||
assert!(s.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cloned_owned_string_should_become_counted_str() {
|
||||
let s: Oco<str> = Oco::Owned(String::from("hello"));
|
||||
assert!(s.clone().is_counted());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cloned_borrowed_str_should_remain_borrowed_str() {
|
||||
let s: Oco<str> = Oco::Borrowed("hello");
|
||||
assert!(s.clone().is_borrowed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cloned_counted_str_should_remain_counted_str() {
|
||||
let s: Oco<str> = Oco::Counted(Rc::from("hello"));
|
||||
assert!(s.clone().is_counted());
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
//! Types that handle asynchronous data loading via `<Suspense/>`.
|
||||
|
||||
use crate::{
|
||||
create_isomorphic_effect, create_rw_signal, create_signal, queue_microtask,
|
||||
signal::SignalGet, store_value, ReadSignal, RwSignal, SignalSet,
|
||||
SignalUpdate, StoredValue, WriteSignal,
|
||||
create_isomorphic_effect, create_rw_signal, create_signal, oco::Oco,
|
||||
queue_microtask, signal::SignalGet, store_value, ReadSignal, RwSignal,
|
||||
SignalSet, SignalUpdate, StoredValue, WriteSignal,
|
||||
};
|
||||
use futures::Future;
|
||||
use std::{
|
||||
borrow::Cow, cell::RefCell, collections::VecDeque, pin::Pin, rc::Rc,
|
||||
};
|
||||
use std::{cell::RefCell, collections::VecDeque, pin::Pin, rc::Rc};
|
||||
|
||||
/// Tracks [`Resource`](crate::Resource)s that are read under a suspense context,
|
||||
/// i.e., within a [`Suspense`](https://docs.rs/leptos_core/latest/leptos_core/fn.Suspense.html) component.
|
||||
|
@ -172,7 +170,7 @@ impl Default for SuspenseContext {
|
|||
/// Represents a chunk in a stream of HTML.
|
||||
pub enum StreamChunk {
|
||||
/// A chunk of synchronous HTML.
|
||||
Sync(Cow<'static, str>),
|
||||
Sync(Oco<'static, str>),
|
||||
/// A future that resolves to be a list of additional chunks.
|
||||
Async {
|
||||
/// The HTML chunks this contains.
|
||||
|
|
|
@ -51,7 +51,6 @@ use leptos::{
|
|||
*,
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
fmt::Debug,
|
||||
rc::Rc,
|
||||
|
@ -100,7 +99,7 @@ pub struct MetaTagsContext {
|
|||
els: Rc<
|
||||
RefCell<
|
||||
IndexMap<
|
||||
Cow<'static, str>,
|
||||
Oco<'static, str>,
|
||||
(HtmlElement<AnyElement>, Option<web_sys::Element>),
|
||||
>,
|
||||
>,
|
||||
|
@ -130,7 +129,7 @@ impl MetaTagsContext {
|
|||
pub fn register(
|
||||
&self,
|
||||
|
||||
id: Cow<'static, str>,
|
||||
id: Oco<'static, str>,
|
||||
builder_el: HtmlElement<AnyElement>,
|
||||
) {
|
||||
cfg_if! {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::use_head;
|
||||
use leptos::{nonce::use_nonce, *};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Injects an [HTMLLinkElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement) into the document
|
||||
/// head, accepting any of the valid attributes for that tag.
|
||||
|
@ -28,62 +27,62 @@ use std::borrow::Cow;
|
|||
pub fn Link(
|
||||
/// The [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-id) attribute.
|
||||
#[prop(optional, into)]
|
||||
id: Option<Cow<'static, str>>,
|
||||
id: Option<Oco<'static, str>>,
|
||||
/// The [`as`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as) attribute.
|
||||
#[prop(optional, into)]
|
||||
as_: Option<Cow<'static, str>>,
|
||||
as_: Option<Oco<'static, str>>,
|
||||
/// The [`crossorigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-crossorigin) attribute.
|
||||
#[prop(optional, into)]
|
||||
crossorigin: Option<Cow<'static, str>>,
|
||||
crossorigin: Option<Oco<'static, str>>,
|
||||
/// The [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-disabled) attribute.
|
||||
#[prop(optional, into)]
|
||||
disabled: Option<bool>,
|
||||
/// The [`fetchpriority`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-fetchpriority) attribute.
|
||||
#[prop(optional, into)]
|
||||
fetchpriority: Option<Cow<'static, str>>,
|
||||
fetchpriority: Option<Oco<'static, str>>,
|
||||
/// The [`href`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-href) attribute.
|
||||
#[prop(optional, into)]
|
||||
href: Option<Cow<'static, str>>,
|
||||
href: Option<Oco<'static, str>>,
|
||||
/// The [`hreflang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-hreflang) attribute.
|
||||
#[prop(optional, into)]
|
||||
hreflang: Option<Cow<'static, str>>,
|
||||
hreflang: Option<Oco<'static, str>>,
|
||||
/// The [`imagesizes`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesizes) attribute.
|
||||
#[prop(optional, into)]
|
||||
imagesizes: Option<Cow<'static, str>>,
|
||||
imagesizes: Option<Oco<'static, str>>,
|
||||
/// The [`imagesrcset`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesrcset) attribute.
|
||||
#[prop(optional, into)]
|
||||
imagesrcset: Option<Cow<'static, str>>,
|
||||
imagesrcset: Option<Oco<'static, str>>,
|
||||
/// The [`integrity`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-integrity) attribute.
|
||||
#[prop(optional, into)]
|
||||
integrity: Option<Cow<'static, str>>,
|
||||
integrity: Option<Oco<'static, str>>,
|
||||
/// The [`media`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media) attribute.
|
||||
#[prop(optional, into)]
|
||||
media: Option<Cow<'static, str>>,
|
||||
media: Option<Oco<'static, str>>,
|
||||
/// The [`prefetch`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-prefetch) attribute.
|
||||
#[prop(optional, into)]
|
||||
prefetch: Option<Cow<'static, str>>,
|
||||
prefetch: Option<Oco<'static, str>>,
|
||||
/// The [`referrerpolicy`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-referrerpolicy) attribute.
|
||||
#[prop(optional, into)]
|
||||
referrerpolicy: Option<Cow<'static, str>>,
|
||||
referrerpolicy: Option<Oco<'static, str>>,
|
||||
/// The [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel) attribute.
|
||||
#[prop(optional, into)]
|
||||
rel: Option<Cow<'static, str>>,
|
||||
rel: Option<Oco<'static, str>>,
|
||||
/// The [`sizes`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes) attribute.
|
||||
#[prop(optional, into)]
|
||||
sizes: Option<Cow<'static, str>>,
|
||||
sizes: Option<Oco<'static, str>>,
|
||||
/// The [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-title) attribute.
|
||||
#[prop(optional, into)]
|
||||
title: Option<Cow<'static, str>>,
|
||||
title: Option<Oco<'static, str>>,
|
||||
/// The [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-type) attribute.
|
||||
#[prop(optional, into)]
|
||||
type_: Option<Cow<'static, str>>,
|
||||
type_: Option<Oco<'static, str>>,
|
||||
/// The [`blocking`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-blocking) attribute.
|
||||
#[prop(optional, into)]
|
||||
blocking: Option<Cow<'static, str>>,
|
||||
blocking: Option<Oco<'static, str>>,
|
||||
) -> impl IntoView {
|
||||
let meta = use_head();
|
||||
let next_id = meta.tags.get_next_id();
|
||||
let id: Cow<'static, str> =
|
||||
let id: Oco<'static, str> =
|
||||
id.unwrap_or_else(|| format!("leptos-link-{}", next_id.0).into());
|
||||
|
||||
let builder_el = leptos::leptos_dom::html::as_meta_tag({
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::use_head;
|
||||
use leptos::{nonce::use_nonce, *};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Injects an [HTMLScriptElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement) into the document
|
||||
/// head, accepting any of the valid attributes for that tag.
|
||||
|
@ -25,47 +24,47 @@ use std::borrow::Cow;
|
|||
pub fn Script(
|
||||
/// An ID for the `<script>` tag.
|
||||
#[prop(optional, into)]
|
||||
id: Option<Cow<'static, str>>,
|
||||
id: Option<Oco<'static, str>>,
|
||||
/// The [`async`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-async) attribute.
|
||||
#[prop(optional, into)]
|
||||
async_: Option<Cow<'static, str>>,
|
||||
async_: Option<Oco<'static, str>>,
|
||||
/// The [`crossorigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin) attribute.
|
||||
#[prop(optional, into)]
|
||||
crossorigin: Option<Cow<'static, str>>,
|
||||
crossorigin: Option<Oco<'static, str>>,
|
||||
/// The [`defer`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer) attribute.
|
||||
#[prop(optional, into)]
|
||||
defer: Option<Cow<'static, str>>,
|
||||
defer: Option<Oco<'static, str>>,
|
||||
/// The [`fetchpriority `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-fetchpriority ) attribute.
|
||||
#[prop(optional, into)]
|
||||
fetchpriority: Option<Cow<'static, str>>,
|
||||
fetchpriority: Option<Oco<'static, str>>,
|
||||
/// The [`integrity`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-integrity) attribute.
|
||||
#[prop(optional, into)]
|
||||
integrity: Option<Cow<'static, str>>,
|
||||
integrity: Option<Oco<'static, str>>,
|
||||
/// The [`nomodule`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nomodule) attribute.
|
||||
#[prop(optional, into)]
|
||||
nomodule: Option<Cow<'static, str>>,
|
||||
nomodule: Option<Oco<'static, str>>,
|
||||
/// The [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce) attribute.
|
||||
#[prop(optional, into)]
|
||||
nonce: Option<Cow<'static, str>>,
|
||||
nonce: Option<Oco<'static, str>>,
|
||||
/// The [`referrerpolicy`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-referrerpolicy) attribute.
|
||||
#[prop(optional, into)]
|
||||
referrerpolicy: Option<Cow<'static, str>>,
|
||||
referrerpolicy: Option<Oco<'static, str>>,
|
||||
/// The [`src`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-src) attribute.
|
||||
#[prop(optional, into)]
|
||||
src: Option<Cow<'static, str>>,
|
||||
src: Option<Oco<'static, str>>,
|
||||
/// The [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type) attribute.
|
||||
#[prop(optional, into)]
|
||||
type_: Option<Cow<'static, str>>,
|
||||
type_: Option<Oco<'static, str>>,
|
||||
/// The [`blocking`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-blocking) attribute.
|
||||
#[prop(optional, into)]
|
||||
blocking: Option<Cow<'static, str>>,
|
||||
blocking: Option<Oco<'static, str>>,
|
||||
/// The content of the `<script>` tag.
|
||||
#[prop(optional)]
|
||||
children: Option<Box<dyn FnOnce() -> Fragment>>,
|
||||
) -> impl IntoView {
|
||||
let meta = use_head();
|
||||
let next_id = meta.tags.get_next_id();
|
||||
let id: Cow<'static, str> =
|
||||
let id: Oco<'static, str> =
|
||||
id.unwrap_or_else(|| format!("leptos-link-{}", next_id.0).into());
|
||||
|
||||
let builder_el = leptos::leptos_dom::html::as_meta_tag({
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::use_head;
|
||||
use leptos::{nonce::use_nonce, *};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Injects an [HTMLStyleElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLStyleElement) into the document
|
||||
/// head, accepting any of the valid attributes for that tag.
|
||||
|
@ -25,26 +24,26 @@ use std::borrow::Cow;
|
|||
pub fn Style(
|
||||
/// An ID for the `<script>` tag.
|
||||
#[prop(optional, into)]
|
||||
id: Option<Cow<'static, str>>,
|
||||
id: Option<Oco<'static, str>>,
|
||||
/// The [`media`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-media) attribute.
|
||||
#[prop(optional, into)]
|
||||
media: Option<Cow<'static, str>>,
|
||||
media: Option<Oco<'static, str>>,
|
||||
/// The [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-nonce) attribute.
|
||||
#[prop(optional, into)]
|
||||
nonce: Option<Cow<'static, str>>,
|
||||
nonce: Option<Oco<'static, str>>,
|
||||
/// The [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-title) attribute.
|
||||
#[prop(optional, into)]
|
||||
title: Option<Cow<'static, str>>,
|
||||
title: Option<Oco<'static, str>>,
|
||||
/// The [`blocking`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-blocking) attribute.
|
||||
#[prop(optional, into)]
|
||||
blocking: Option<Cow<'static, str>>,
|
||||
blocking: Option<Oco<'static, str>>,
|
||||
/// The content of the `<style>` tag.
|
||||
#[prop(optional)]
|
||||
children: Option<Box<dyn FnOnce() -> Fragment>>,
|
||||
) -> impl IntoView {
|
||||
let meta = use_head();
|
||||
let next_id = meta.tags.get_next_id();
|
||||
let id: Cow<'static, str> =
|
||||
let id: Oco<'static, str> =
|
||||
id.unwrap_or_else(|| format!("leptos-link-{}", next_id.0).into());
|
||||
|
||||
let builder_el = leptos::leptos_dom::html::as_meta_tag({
|
||||
|
|
|
@ -16,11 +16,11 @@ pub struct TitleContext {
|
|||
|
||||
impl TitleContext {
|
||||
/// Converts the title into a string that can be used as the text content of a `<title>` tag.
|
||||
pub fn as_string(&self) -> Option<String> {
|
||||
let title = self.text.borrow().as_ref().map(|f| f.get());
|
||||
pub fn as_string(&self) -> Option<Oco<'static, str>> {
|
||||
let title = self.text.borrow().as_ref().map(TextProp::get);
|
||||
title.map(|title| {
|
||||
if let Some(formatter) = &*self.formatter.borrow() {
|
||||
(formatter.0)(title)
|
||||
(formatter.0)(title.into_owned()).into()
|
||||
} else {
|
||||
title
|
||||
}
|
||||
|
|
|
@ -24,6 +24,20 @@ impl ToHref for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToHref for Cow<'_, str> {
|
||||
fn to_href(&self) -> Box<dyn Fn() -> String + '_> {
|
||||
let s = self.to_string();
|
||||
Box::new(move || s.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToHref for Oco<'_, str> {
|
||||
fn to_href(&self) -> Box<dyn Fn() -> String + '_> {
|
||||
let s = self.to_string();
|
||||
Box::new(move || s.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ToHref for F
|
||||
where
|
||||
F: Fn() -> String + 'static,
|
||||
|
@ -65,7 +79,7 @@ pub fn A<H>(
|
|||
/// `[aria-current=page]` selector, you should prefer that, as it enables significant
|
||||
/// SSR optimizations.
|
||||
#[prop(optional, into)]
|
||||
active_class: Option<Cow<'static, str>>,
|
||||
active_class: Option<Oco<'static, str>>,
|
||||
/// An object of any type that will be pushed to router state
|
||||
#[prop(optional)]
|
||||
state: Option<State>,
|
||||
|
@ -78,7 +92,7 @@ pub fn A<H>(
|
|||
class: Option<AttributeValue>,
|
||||
/// Sets the `id` attribute on the underlying `<a>` tag, making it easier to target.
|
||||
#[prop(optional, into)]
|
||||
id: Option<String>,
|
||||
id: Option<Oco<'static, str>>,
|
||||
/// The nodes or elements to be shown inside the link.
|
||||
children: Children,
|
||||
) -> impl IntoView
|
||||
|
@ -95,37 +109,35 @@ where
|
|||
#[allow(unused)] state: Option<State>,
|
||||
#[allow(unused)] replace: bool,
|
||||
class: Option<AttributeValue>,
|
||||
#[allow(unused)] active_class: Option<Cow<'static, str>>,
|
||||
id: Option<String>,
|
||||
#[allow(unused)] active_class: Option<Oco<'static, str>>,
|
||||
id: Option<Oco<'static, str>>,
|
||||
children: Children,
|
||||
) -> View {
|
||||
#[cfg(not(any(feature = "hydrate", feature = "csr")))]
|
||||
{
|
||||
_ = state;
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "hydrate", feature = "csr")))]
|
||||
{
|
||||
_ = replace;
|
||||
}
|
||||
|
||||
let location = use_location();
|
||||
let is_active = create_memo(move |_| match href.get() {
|
||||
None => false,
|
||||
|
||||
Some(to) => {
|
||||
let is_active = create_memo(move |_| {
|
||||
href.with(|href| {
|
||||
href.as_deref().is_some_and(|to| {
|
||||
let path = to
|
||||
.split(['?', '#'])
|
||||
.next()
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
let loc = location.pathname.get().to_lowercase();
|
||||
location.pathname.with(|loc| {
|
||||
let loc = loc.to_lowercase();
|
||||
if exact {
|
||||
loc == path
|
||||
} else {
|
||||
loc.starts_with(&path)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
use leptos::{leptos_dom::Transparent, *};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
};
|
||||
|
@ -309,7 +310,7 @@ impl RouteContext {
|
|||
|
||||
pub(crate) fn resolve_path_tracked(&self, to: &str) -> Option<String> {
|
||||
resolve_path(&self.inner.base_path, to, Some(&self.inner.path.get()))
|
||||
.map(String::from)
|
||||
.map(Cow::into_owned)
|
||||
}
|
||||
|
||||
/// The nested child route, if any.
|
||||
|
|
|
@ -544,6 +544,7 @@ pub(crate) fn create_branch(routes: &[RouteData], index: usize) -> Branch {
|
|||
score: routes.last().unwrap().score() * 10000 - (index as i32),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
tracing::instrument(level = "info", skip_all,)
|
||||
|
@ -567,7 +568,7 @@ fn create_routes(route_def: &RouteDefinition, base: &str) -> Vec<RouteData> {
|
|||
id: route_def.id,
|
||||
matcher: Matcher::new_with_partial(&pattern, !is_leaf),
|
||||
pattern,
|
||||
original_path: original_path.to_string(),
|
||||
original_path: original_path.into_owned(),
|
||||
});
|
||||
}
|
||||
acc
|
||||
|
|
|
@ -4,9 +4,9 @@ use crate::{
|
|||
};
|
||||
use leptos::{
|
||||
create_memo, request_animation_frame, signal_prelude::*, use_context, Memo,
|
||||
Oco,
|
||||
};
|
||||
use std::{borrow::Cow, rc::Rc, str::FromStr};
|
||||
|
||||
use std::{rc::Rc, str::FromStr};
|
||||
/// Constructs a signal synchronized with a specific URL query parameter.
|
||||
///
|
||||
/// The function creates a bidirectional sync mechanism between the state encapsulated in a signal and a URL query parameter.
|
||||
|
@ -45,7 +45,7 @@ use std::{borrow::Cow, rc::Rc, str::FromStr};
|
|||
/// ```
|
||||
#[track_caller]
|
||||
pub fn create_query_signal<T>(
|
||||
key: impl Into<Cow<'static, str>>,
|
||||
key: impl Into<Oco<'static, str>>,
|
||||
) -> (Memo<Option<T>>, SignalSetter<Option<T>>)
|
||||
where
|
||||
T: FromStr + ToString + PartialEq,
|
||||
|
@ -163,7 +163,7 @@ pub fn use_resolved_path(
|
|||
if path.starts_with('/') {
|
||||
Some(path)
|
||||
} else {
|
||||
route.resolve_path_tracked(&path).map(String::from)
|
||||
route.resolve_path_tracked(&path)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::borrow::Cow;
|
|||
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub fn expand_optionals(pattern: &str) -> Vec<Cow<str>> {
|
||||
pub fn expand_optionals(pattern: &str) -> Vec<Cow<'_, str>> {
|
||||
use js_sys::RegExp;
|
||||
use once_cell::unsync::Lazy;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
@ -58,7 +58,7 @@ pub fn expand_optionals(pattern: &str) -> Vec<Cow<str>> {
|
|||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "ssr")]
|
||||
pub fn expand_optionals(pattern: &str) -> Vec<Cow<str>> {
|
||||
pub fn expand_optionals(pattern: &str) -> Vec<Cow<'_, str>> {
|
||||
use regex::Regex;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
|
Loading…
Reference in a new issue