- Add a prelude to import all html tags, svg tags, html attributes, svg attributes that doesn't conflicts
- Make a module that isolate the
with-dom
features. - Rework the dumb patch taking the advantage of feature gating the dom capability.
- Add documentation to functions
- Add examples to usage of methods in
Program
.
- Add examples to usage of methods in
- Loosen the lifetime requirement of the
Fn
insideCallback
from'static
to a generic one eg:'c
- Done in
mt-dom
branch: non-static-lifetime
- Done in
- Deprecate the tag macro since it complicates the conflict in reexporting the functions
- ie:
style!
as a tag,style!
macro for attributes,style
as attribute call.
- ie:
- Change the README example to use the node macro syntax
- rename the old
minimal
tominimal-alt
and use thenode-macro-syntax
inminimal
example
- rename the old
- Move
sauron-syntax
intohtml2sauron
project [ ] Expose Cmd,Component outside ofwith-dom
feature gate- This would allow a total isomorphic app reusing the components
[ ] Make an equivalent for Program(client-side updater) for use in server-side~~ - ie: ServerRender, where Msg could be passed as a data to hydrate the view (template) before sending to the client~~- We don't need to use the update function in server-side rendering. We set the state of the app by instatiating the app with appropriate data.
- Fix the render function where attributes of the same name not merged
- Change type of tag, attribute_name, style keys from
&'static str
to&'a str
- This will remove the need for hardcode HTML_STYLES lookup, which could be a performance penalty
- Add the RealWorld example
- Use the elm base code https://github.com/rtfeldman/elm-spa-example
~~- [ ] breaking
put back, to avoid possible confusion to new users.style
as a normal attribute - Cancelled, since style is treated differently in attributes. ~~
- Use the elm base code https://github.com/rtfeldman/elm-spa-example
~~- [ ] breaking
- breaking merge
Browser
toWindow
. - Add
and_then
,sequence
toCmd
to perform a task after the preceding Cmd succeeds. - Create a document on why it is needed for events such as
on_click
to have a copy of the variables outside of its environment. - Rethink on the naming of Component, SimpleComponent, SubComponent.
- Component is actually Application since it the app that is manipulated by the program(executor).
- Other names:
RootApplication, Component,Control,Widget
- Merge
init
andinit_with_program
- It make sense to make
init
mutable so the developer can manipulate the component at initialization stage. - Make call to
init
right after the Application is mounted into theDOM
, rather than before. - Simplify
Application::init
to not have access toProgram
since it can return aCmd
. It can however, modify itself.
- It make sense to make
- Rename the type alias
Callback
intoEventCallback
orListener
. This way, we can use the more genericCallback
in Components and inCmd
.- Recreate Callback from a clean state, with no TypeId and used it in
Cmd
. - Listener will have it's own dedidate struct with the TypeId.
- Use
Callback
inCmd
- Recreate Callback from a clean state, with no TypeId and used it in
- Component system declared in view.
-
The current system needs to store all state of the Application and its member sub components, regardless if they are specific to the Aplication or not.
-
Some component will have properties that the App don't need to store.
-
To do this, we need to create higher level macro and function which includes Component to be a node variant.
enum Msg{ FuiButtonMsg(fui_button::Msg), } fn view(&self) -> Node<Widget>{ <div class="wrapper"> <FuiButton on_click=|_|Msg::BtnClicked style="full"/> </div> }
leaf.rs
pub enum Leaf<MSG>{ Text(String), <...> Component(Box<dyn Component>), <...> }
attribute_value.rs
pub enum AttributeValue<MSG>{ Simple(Value), CompProperties(<ComponentProperties>), }
-
The
Application
don't have to store the state ofFuiButton
component, it will be stored into theProgram
object.- Issue how will be map the Msg of the sub component to convert it into the Msg of the main
Application
?
- Issue how will be map the Msg of the sub component to convert it into the Msg of the main
-
Merge the Container and Component which the view is now requires to have children components
-
Add a CustomElement trait which facilitates the component to be a custom element
-
Rethink of the sauron-component-macro
- Redo it, maybe we don't need it and then manually implement all the Components
[ ] Make Application trait for internal usage only
-
- Make Http api pass a decoder function
- Additional to the dispatching of mount event.
- on_mount
- on_will_mount
- on_dismount
- on_will_dismount
- on_mount
- Make the mount event be wrap as a real event, this way we can dispatch it in the real dom instead of from the virtual node
let mount_event = new Event("mount"); elm.dispatchEvent(mount_event);
- Call set_attribute in addition to setting the special attributes such as
value
,checked
, this should trigger theattribute_changed
callback in web components - The attribute_changed method in CustomElement should return an
MSG
which will be dispatched in the WebComponent struct. - There is conflict with the use of
style
style!
macrostyle
attribute functionstyle
method in Application, Component, Containerstyle
html tag Maybe usecss
orstylesheet
as the method name.
fn css(&self) -> Vec<String>{ } fn stylesheet(&self) -> Vec<String>{ }
- Make the compilation error in
jss!
,style!
, more informative - Optimize handling of style by diffing each style properties
- Update only specific stype instead of setting the whole style attributes
-
[ ] Find a way to mapCmd<APP,MSG>
toCmd<APP2, MSG2>
ie:Cmd<ChildApp, ChildMsg>
toCmd<App, Msg>
This is needed sinceCmd
fromupdate
function of sub components are not dispatched in the program. Only the top level componentCmd
can be dispatched[ ] Find a way to mapProgram<APP,MSG>
toProgram<APP2,MSG2>
[X] mapDomUpdater<MSG>
toDomUpdater<MSG2>
Issue mapping fields of Program that are in~~ as the Rc value of dom_updater is to be borrowed and will have a borrow checker issue~~Rc<RefCell>
seems not that simple
-
Merge
Program
andDomUpdater
- Issue: DomUpdater has multiple fields, which would then be wrap with
Rc<RefCell>
individually
- Issue: DomUpdater has multiple fields, which would then be wrap with
-
[ ] Change the'static
of trait implementation by specifying the lifetime - ref: https://stackoverflow.com/questions/52187644/lifetime-must-be-valid-for-the-static-lifetime-so-that-the-types-are-compatible -
Get rid of test_fixtures and move it to test directory
-
Make each component have a reference to the root dom where it is mounted.
- This will make local state changes to the component easier to do, as opposed to diffing the whole DOM tree.
-
Unify the code of Program replace_mount, append_mount
-
[ ] replace the request_animation_frame with the code fromexecute_request_animation
frame -
Create a function to derive Component name from the struct name of the Component and preprocess the jss with it before injecting it to the main program
-
Clean up
CreateNode
- no need to wrap
Node
andElement
instead just return them as created with theirclosures
- no need to wrap
-
Cmd should include a
should_update: bool
field which indicates if the update should be made or not - Cmd{ commands:Vec<..>,should_update } - Cmd::noop() // no update operation - Fixed in0.36.0
-
Remove the Dispatch trait and pass Program as it is in
dom_updater
andapply_patches
module- There is only one implementation of
Dispatch
trait anyway, that isProgram
- Dispatch serve its purpose to make the code less clutter, by passing arguments around with less generics.
- There is only one implementation of
-
ISSUE: sauron
node!
macro doesn't work on svg tags since it is using onlyhtml_element
function whichnamespace
is not supplied.- Fixed in
0.35.0
by checking whether a tag has a namespace.
- Fixed in
-
Change
program: Option<&DSP>
to justprogram: &DSP
since there program is needed everywhere. -
Improve
sauron-node-macro
to call on the equivalent html function instead ofelment_ns
.- This would improve performance since the function already has the information whether or not it has namespace or not.
- Mitigated with the use of
Lazy
HashSet
look up insauron-parse
for faster lookup. - Further improved using
sauron-parse
by resolving the value ofself-closing
andnamespace
at compile time in thenode
macro.
-
Create a hashed collections in
sauron-parse
to optimize lookup of tags for namespace or self-closing- Created a fast lookup for
is_self_closing(tag)
amdtag_namespace
.
- Created a fast lookup for
-
old elements that has an event attached has no way of knowing the equivalent new element has the same event attached as their callbacks are clone of closures inside of Rc and no 2 closures are the same even if the have the same code.
-
~~[ ] add a conditional function for event attribute that if any of the other attribute is changed the event will have to be remove and re-attach.
- This is to mitigate the aggressive recycling of nodes which we skipp diffing for event listeners for performance reasons, as it is impractical to reattach event listener at every render cycle.~~
- This has been solved by using the
TypeId
of the closure of the callback.
-
Remove NodeIdx traversal and also remove NodeIdx in
mt-dom
TreePath, as traversal path prove to be correct. -
Maybe Remove the style functionality as Components and Applications can manipulate the style in the document directly
- Change style that it returns only a
String
instead ofVec<String>
. - The injected style shall have a class name equal to the the type_id of the
APP
.
- Change style that it returns only a
-
Add
maybe_attr(name: &str, value: Option<Value>)
to set the attribute if there is a value. empty otherwise. -
Centralize the handling of attributes tha has a state such as
value
,checked
,. -
Issue with not finding the nodes to be patched
- This issue manifested in
performance-test-sauron
repo - Suspecting it has to do with
mount_node
androot_node
as replace and append could have a different behavior in the 2. - Solved by: using mutable reference to the
root_node
rather than a mutable reference to a clond one.
- This issue manifested in
-
Rethink about the
replace_mount
in Program- It is useful for replacing the preload spinner when the application is finished loading
- Have an enum for mount action
enum MountAction{ /// append as child to the target mount Append, /// clear any child to the target mount then append ClearAppend, /// replace the target mount with the root node Replace }
- Mount event should have a reference to the
host_node
and theroot_node
- host_node is the node where the view is mounted, usually the parent - in case of replace host_node is the same as the root_node.
-
Maybe we don't need the
async
in update. -
[ ] Refactor Program that will have to use less ofRc<RefCell<>>
, by having an inner structure which is wrapped intoRc<RefCell<>>
- this is not possible because we need to update each field separately, and borrowing the inner program state will disallow borrowing other fields.
-
BUG: if the
dispatch_inner
is not called in a callback which isrequest_animation_frame
orrequest_idle_callback
- This will cause the
dispatch_mount
event to dispatch before theroot_node
is set in the program when the program is to be mounted - Note the
dispatch_mount
is triggered when the view hason_mount
event. - mitigation: make the
dispatch_inner
spawn in a thead either via callback, orspawn_local
fromwasm_bindgen_futures
.
- This will cause the
-
Tighten visibility of objects that are not meant to be
pub
- some fields in
Program
- struct types that are not meant to be public
- some fields in
-
Use the deadline object in
request_idle_callback_with_deadline
, instead of justf64
, which calculates the remaining time manually -
Migrate to rstml, since syn-rsx is unmaintained.
-
Remove
Dispatch
and just passProgram
around -
Make an alternative to
Effects
andCmd
that can be used inComponent
.- call it
Task
a wrapper to a future, will resolve into MSG which will then be dispatched into the program - does not have access to program for dispatching
- call it
-
Remove the use of
Closure::forget()
-
Refactor
ActiveClosure
to use - add a fielddom_closures
inProgram
which stores all closure for a certain Element - all other closures is stored inactive_closure
rust closure_id_counter: usize, type ActiveClosure: BTreeMap<usize, Closure>;
-
unify the
Program::add_event_listener
which attach the event to window and thedom_node::add_event_listener_callback
usage used inset_element_attributes
Program::add_event_listener(&self, target_element: EventTarget, event_listeners).
-
Make the svg attributes follow
snake_case
conventionviewBox
->view_box
preserveAspectRatio
->preserve_aspect_ratio
-
As an alternative to Task where
Component
can not useCmd
, due to it referencing Program, we can instead return listeners.- window listeners
- document listener
struct GlobalListener{ window_listeners: Vec<Attribute<MSG>>; document_listeners: Vec<Attribute<MSG>>; }
add these events:
on_interval(|i32|{})
for attaching interval in the Window Http can be done with task
-
Make Http functions return a Task
-
Make
Sub
as counterpart toCmd
- We can use
Sub
in theComponent
fn on_resize(&self) -> Sub<Msg>{ }
- We can use
-
Bring back CreatedNode maybe with a different name:
DomNode
which wraps theNode
orElement
along with it's closures from event listener- These are then saved into the
Program
where when removed or dropped, it will also drop the associated closures with them, thereby simplifying the code. - Right now, we are attaching a
vdom-data
attribute for nodes that have listeners
- These are then saved into the
-
Make use of
Arc<RwLock>
to check if can solve copying theAPP
viatransmute_copy
.it didn't solve it- See if there are performance penalty
-
Make template node for when app view is first created
- The template is then patched when the app is mounted.
- Templates will have to be saved in the global context, so it can be used accross multiple programs
- Using the template was slow, Find out which part is the program spending much time. check the time in the diffing
- Most of the time is spent on applying the patches
- Make use of prediff to shorten diffing on nodes that are full static
- unify the code where template is patched and converted
-
Create a
view!
macro which generated the view function + template + prediff functions -
Make a variant of
Node
leaf to beComponent
, component can contain attributes and optionally children components + elements- This requires moving the mt-dom types into sauron-core so as to make a specific diff function for component
-
put back map_msg module where it contains map_msg functions for Node, Element, Attributes
- this way, it will be easier to see the flow of map_msg method
-
remove
is_static
methods as they are not useful to template patches anymore -
process the patches in the component when attributes and children are removed or added.
-
Make an additional diffing algorithm for the template blocks
- This is anchored as next to the last sibling
- This can not just be backtrack to the parent, as it defeats the purpose of doing as little diff as possible.
-
Make a TemplateView struct and a variant of Leaf
struct TemplatedView<MSG>{ template: Box<dyn Fn() -> Node<MSG>>, skip_diff: Box<dyn Fn() -> SkipDiff>, view: Node<MSG> , }
-
remove
SafeHtml
variant in leaf, instead provide a safe_html which is parsed and converted into node- using safe html alters the dom tree
-
add
symbol_html
for html entities such as
>
,<
etc.- this should be safe to be inserted
-
remove
innerHTML
func in AttributeValue as it could alter the DOM node tree -
Keep track of which attributes to be skipped in
SkipDiff{shall:false}
enum SkipStrat{ SkipAll, SkippIndex(Vec<usize>), }
-
Maybe disable the template usage for now
-
Make
Cmd
to be used internally as it needs reference to theProgram<APP>
- Use
Task
for returning fromApplication
init andupdate
. - The
Recurring Task
is actually just a Sub in elm- Issue with recurring task, how to store the closures which has different multiple types for the in arguments
- Store the closures with
Closure<dyn Fn(IN)->MSG>
- Then task becomes
Task<IN,MSG>
- The
SingleTask
is a Cmd in sauron - Rename
Cmd
toCommand
alternative:Action
,Operation
,Instruction
,Effects
,Dispatch
- This is effects in elm
- Rename
Cmd
toDispatch
- Rename
SingleTask
toAction
- Rename
RecurringTask
toSub
- enum Command{Cmd,Sub} into one unified type.
- Cmd is a vec of Command
- Sauron just consolidate them into one enum struct for simplicity
- Use
-
Remove
Modifier
andmeasurements
-
Unify
vdom::Node
anddom::DomNode
- pro: This way stateful component can also be rendered server-side
- pro: The node can be patch in the server-side
- con: usage of
Rc
andRefCell
on the components- Need to have a reference to Parent to apply
replace_node
- The Patch patch can get a reference to the parent via backtrack()
- Need to have a reference to Parent to apply
-
Issue with attribute in stateful component
attribute_changed
method is not changing the view as it has no access to the program to dispatch some commands.- Maybe stateful_component need to have access to its program
- Maybe Application will also need to have access to its program
create its own program and mount itself with
App::new().mount_to_body()
-
Remove
use-teamplate
as theDomNode
is now a wrapper, this defeats the purpose of making a faster dom via template as deep clone has to also wrap the children nodes
- Storage service (May not be needed since the user can directly use web-sys)
- using wasm-bindgen directly will remove the need for Storage service wrapper
- Fetch service
- Url change service
- using wasm-bindgen directly eliminates the need for Url change service wrapper
- re-think about the
sauron-core
features:-
with-dom
when used in client-side, default []with-ssr
when used in server-side rendering, mutually exlusive towith-dom
- Server-side rendering is implicit when target is not wasm.
-
no_request_animation_frame
this should be additive- crate is now using
with-request-animation
feature
- crate is now using
-
-
with-markdown
- Add sanitation to markdown parser, use
ammonia
crate - expose the
sauron-md
assauron::markdown
module, behind a feature flag
- Add sanitation to markdown parser, use
- Add example using markdown
- Make use of
serde_json
to parsestyle
into components - Add an example where a program is a custom html element, that way sauron could be used as a way to migrate parts of an existing html/js code base.
- Custom element which is defiend as a web component where it can be used by some other Application.
- The App should be serializable and each of the fields will become an html attribute which
- There is an issue with the patch not being able to find the element to be patch when using custom element due to the reason that the root_node stored in the dom updater is not the first element of the view, but rather the root_node in the dom updater is the first element of the view. The old technique was the replace the root node with the created first element but this is not ideal when used for custom_element since we need to get the attributes from the custom element Possible solution: - Add a mount_node to the dom_updater alongside with the root_node
- custom element would be appended to the
shadowRoot
- Usage of custom element inside another sauron component should skip the custom-element internal DOM elements
- custom element should also need access to the
textContent
of the tag for further processing
- Properly trigger the MountEvent at the appending of the component to the DOM
- Right now, it is triggered when the virtual Node is created into a real Node.
- Maybe rename
#[web_component]
macro to#[custom_element]
- Also
WebComponent
toCustomElementWrapper
- Also
- Implement the
StatefulComponent
- attributes and attribute value (key)
- removing children and setting attributes
- Make the fancy-ui example work
[X] Change Patch to use PatchTarget, where TargetNode will be specified for StatefulComponentsThis is necessary since StatefulComponents child container is not necessarily the root node.- Alternative way is to convert child_container to TreePath by searching for it from the root node
- Rename local variables to their descriptive name
- batch_pending_cmds -> batch_pending_dispatch
- init_cmd -> init_dispatch;
- dispatch
attribute_change
for all attributes in stateful component
- Fix the reported issues with benchmarks
- fixed by setting the target to web when building the wasm
- Create a new benchmark for the js-comprehensive-benchmark suite
- link
- Initial attempt https://github.com/ivanceras/performance-test-sauron
- Use Weak pointer for program instead of Rc where strong reference is not needed. - Program stays as long as the user is using the app.
- Add
Program::batch_dispatch(&self, msgs: Vec<MSG>)
to call update on each of the messages before calling on the view, this would improve performance when there are multiple messages to be dispatched to the application- implemented with
dispatch_multiple
- implemented with
- Make a benchmark for building views with more than 2000 nodes, like a text-editor.
- There is a huge performance regression in between 0.40 and 0.42
- It was cause by jss
style!
macro where the lookup for style name is recreated everytime, due to the use ofconst
instead ofstatic
in aonce_cell::Lazy
declaration. This is fixed injss 0.3.3
- Create the dom nodes in depth-first-traversal
- Make a pending patches in the DomUpdater which stops applying remaining patches when the time remaining for the deadline is up.
- There is a problem with storing the patch in a struct which will need to have explicit lifetime
- This can not also be stored in a global variable since there is a generic MSG
- Solution:
- Make an owned Patch which has no reference to the node
struct OwnedPatch{ tag: Option<TAG>, node: Node<'a,...>, }
- Store the patches as Closures, so as to get away with the generics
- but then there will be error can be shared between threads because closure is not Send
- Make a DomPatch which a DOM version of the patch with the target node and created node
- Make an owned Patch which has no reference to the node
- Find a way to break up building the view into multiple frames, since view can take a long time to build
- Find a way to break up diffing the current vdom and the new vdom as they can also take a bit of long time as well.
- Add benchmark function for using CACHE_ELEMENT and not
- Make dispatch pending patches break when the animation frame timeouts, same way as dispatching pending msgs
- Check the last time the dom is updated, if it is less than 17ms, delay the dom update until 17ms has elapsed since the last update.
- Make use of talc allocator for faster and leaner memory
- Move
sauron-markdown
into it's own repo, for keeping sauron slim. - Move
jss
into a new cratesauron-jss
for keeping sauron slim.- Use json crate for
jss
.- The quote on keys are optional, so this is good for use in writing css.
- Use json crate for
- Enumerate the exported modules and structs in prelude instead of just using glob(ie: *).
- Fix the data-viewer example to use Components on the views rather than Application
- Revisit and use style_name identifier in usage of jss in examples
- Move
html::units
tojss
crate - Rename
DomUpdater
toDomPatcher
.- move apply_patches into
DomPatcher
.
- move apply_patches into
- Rename
CreatedNode
toDomNode
.- Maybe completely remove CreatedNode
- Move fields from
DomUpdater
intoProgram
such as- current_vdom
- root_node,
- active_closures,
- pending_patches
- Remove the use of
wee_alloc
crate - sauron-core and jss should have a different version of Value, where jss value can be converted into.
Value
struct needs to reside here, since it is a corner-stone data structure and used eveywhere.- Maybe
Value
should be in a very common crate. Saysauron-common
.
- Use
xtask
for scripting: building, checking, publishing, running the examples - Use
{ workspace = true }
to common dependencies for easieir maintenance work.
- When 2 nodes with multiple similar keys, multiple replace node patch is generated. But it couldn't seem to find the correct target element.
or the target element has no parent, therefore can not replace/insert the node.
- This is solved by getting the type_id of the closure.
- Add more test for recycled nodes with keys
- When 2 text are next to each other, the second text will become a comment
- Runtime errors when using fragments
- usage of
classes_flag
seems to be broken with complext trait requirement.- This should work very simply
classes_flag([("todo", true), ("editor", is_editing)])
- This should work very simply
- Organize the generated docs, such that the most commonly used structs and enums are displayed on the first page of docs.rs/sauron instead of under prelude module
In rust, no two closures, even if identical, have the same type. Therefore closure can not be check for equality.Solved by using the original type_id of the function callback- In sauron node are matched and reused aggressively, except when the keys are different then the node is discarded.
- If we don't reuse nodes with event listeners aggressively, then we would have a performance penalty, since every recreation of the node with event listener will have to discard and create a new one for it, even if it is matching itself.
- Adding
key
attribute provides a good trade off.