RoadMap
| Plan | Date |
|---|---|
| Virtual-DOM | 2020.1.28 - 2020.2.1 |
| Basic-Component | 2020.2.5 - 2020.2.7 |
| Router | 2020.2.7 - 2020.2.10 |
| Native Components | 2020.2.10 - 2020.2.11 |
| Benchmark | 2020.2.11 - 2020.2.15 |
| Beta Versin | 2020.2.15 - 2020.2.17 |
Calling Elvis ๐ฆ ๐ธ ๐ธ ๐ก ๐ ๐ช ๐ฐ
Is anybody home?
As we know, Elvis is a famous rock star, and both a famous rock song named Calling Elvis wrote by Dire Straits which inspired a unknown rock star to write down these chords(code || words).
For now, Elvis, the rock star, will rise, once again, beyond the internet โโ truely your wasm web library, ๐ฆ + ๐ธ => ๐
The Evlis Book mainly talks about the usage of the npm package calling-elvis, and if you want to use "low-level" api rusting the web, plz check out elvis' rust doc.
Goals ๐ฏ
Writing web pages in pure javascript using wasm bindings, without jsx or any other complex syntax, just javascript, of course, not writing html nor css either.
Roll up for the Magical Mystery Tour! ๐
๐งโโ๏ธ ๐คนโโ๏ธ Here we go! Roll up, roll up for the mystery tour, the magical mystery tour is waiting to take you away! Hoping to take you away! Coming to take you away! Dying to take you away, take you today! ๐ธ
๐ป Let me take you down, cause I'm going to,
/* my elvis file */
import { Colors, Elvis, Text } from "calling-elvis";
const Home = Text("Pink is the Pig!", {
bold: true,
italic: true,
size: 10,
color: Colors.PinkAccent(),
});
new Elvis({
home: Home,
}).calling();
Strawberry Fields ๐งโ๐
LICENSE
Heartbreak Hotel.
Widgets
Widgets coming as they are!
Elvis just provides Image and Text for now, we've got apis to compose new Widgets in rust api, join us if you like calling Elvis.
#![allow(unused_variables)] fn main() { /// Elvis' Attribute is cool. /// /// The no-cool part is, Elvis has a big Attributes Enum system, /// it's more responsible to use enum than chars. pub struct Attribute { key: Attributes, value: AttributeValues, } /// Abstract ElvisElement pub trait ElvisElement { // give birth to a new life fn append(&mut self); // deserialize html string to ElvisElement fn de(s: String) -> ElvisElement; // call her/his father fn father(&self) -> ElvisElement; // if you don't have a girlfriend, new one. fn new<T>() -> T; // serlalize ElvisElement to html string fn ser(&self) -> String; } /// Love is All you Need! /// /// Speaking easily, you don't need to write the trait above, /// Elvis has a proc-macro to impl the trait defaultly, /// just paint your child. #[derive(ElvisElement)] pub struct MyElement { css: String, tag: String, attrs: Vec<Attribute>, child: Arc<AsRef<ElvisElement>>, } }
Javascript
It's quite intresting dressing widgets up for a new face, we can do anything changing Elvis we want in Javascript, it depends on if you have the power to save the world, as we know, you are the one.
Image
/* Image */
import { Center, Elvis, Text, Image } from "calling-elvis";
// Generate a `Image`
const myImage = Image(
"https://images-assets.nasa.gov/image/S65-34635/S65-34635~orig.jpg",
Text("hallo, spaceboy!"),
);
Elvis.call(Center(myImage));
If you don't want Image playing in background anonymously, just remove the child field.
Text
/* Text */
import { Center, Colors, Elvis, Text } from "calling-elvis";
Elvis.call(
Center(
Text("Calling Elvis", {
bold: true,
color: Colors.PinkAccent(),
italic: true,
size: 42.0,
weight: 700,
height: 20,
stretch: 100,
}),
),
);
Text might be the most popular spider from Mars, Does it know the Great Ziggy Stardust?
DOM
Note: Elvis just render static html pages now, DOM is in progress.
Like the other UI libraries, webassemblly Elvis arms a Virtual-DOM, too.
import { Center, Elvis, GestureDetector, State, Text } from "calling-elvis";
class MyState extends State {
state = {
count: 0,
}
create() {
console.log("The first step is creating the dom.");
}
update() {
console.log("Then we can update it, just like what you saw.");
}
render() {
return Center(
GestureDetector(
Text("I'm a Elvis fans."),
onTap: () => this.setState({
count: this.count + 1
});
),
);
}
drop() {
console.log("Don't be crue, if you want to drop me.");
}
}
I won't tell you that I'm not only a React fan but also a Flutter fan, and you'll never know I'm a Emacs fan.
LifeCycle
Already knew you care about lifecycles, Elvis will never force you to recite 11 or more methods, Elvis just got 3, and it is enough ; )
Furthermore, to be serious, we don't recommend you to write big projects such as facebook, reddit, or twitter calling Elvis, Elvis is under Proof-of-Concept for now, and...you know, we strongly recommend you to use Elvis building your persenol website, make the web better, more interesting, awesome as it has never been ๐
And if you are building a rocky start-up project, believe me, CALLING ELVIS, and the force will be with you!
Life Story, Love and Glory.
Diff
Elvis' diff algorithm is quite simple flying with wasm, we compare the new node and the old one using dps, and then patch the updates to the old one.
A TreeNode in Elvis is like this:
#![allow(unused_variables)] fn main() { #[derive(Clone, Eq, PartEq)] pub struct TreeNode { css: CSS, tag: String, attrs: Vec<Attribute>, children: Vec<Rc<AsRef<TreeNode>>>, } }
Patch
Elvis prefers to cure the naughty node's father node, if there are complex changes inside it, for Example:
<father :="I'm the naughty nodes' father">
<naughty>Up</naughty>
<naughty>Side</naughty>
<naughty>Down</naughty>
</father>
Now we upside down the Up, Side, Down List:
<father :="plz don't make me heartbreak for twice.">
<naughty>Down</naughty>
<naughty>Side</naughty>
<naughty>Up</naughty>
</father>
Elvis with not trying to swap <li>Up</li> and <li>Down</li>, it will operate DOM twice over, we just replace the whole <ol>...</ol> for once.
Events
Note: In progress.
Follows MDN docs Events
Events share properties to State, kind like a third-part plugin in Elvis, it implelements both in rust and typescript.
Javascript
/* Events */
import { Component, Elvis, Events } from "calling-elvis";
const { Text } = Elvis;
class myClickAbleText extends State {
state = {
msg: "",
}
consturstor(msg: string) {
this.msg = msg;
}
click() {
window.alert(`{}, roger that!`);
}
render() {
return Text("Click me plz.");
}
}
Elvis Events are outside of native components, we writing them straight just like writing lifecycles. Elvis strongly recommands our folks writing persnal components even publishing them to github.
Rust
#![allow(unused_variables)] fn main() { pub enum Events { Cancel, Error, Scroll, Select, Show, Wheel, // Clipborad events Copy, Cut, Paste, // Composition events CompositionEnd, CompositionStart, CompositionUpdate, // Focus events Blur, Focus, Focusin, Focusout, // Fullscreen events KeyDown, KeyPress, KeyUp, // Mouse events AuxClick, Click, ContextMenu, DbClick, MouseDown, MouseEnter, MouseLeave, MouseMove, MouseOut, MouseOver, MouseUp, WebkitMouseForceChanged, WebkitMouseForceDown, WebkitMouseForceWillBegin, WebkitMouseForceUp, // Touch events, TouchCancel, TouchEnd, TouchMove, TouchStart } }
Events enum list all events in MDN events, the implementation in rust depends on gloo.
Gestures
Note: In Progress.
Event and Gesture are out of wasm, this is not cool, but still awesome, Elvis implements these two trouble maker in Typescript, because wasm-bindgen doesn't support passing javascript Object to rust for now, BUT, it will be totally cool one day.
GestureDetector
#![allow(unused_variables)] fn main() { pub trait GestureDetector { fn on_double_tap(e: Event); fn on_force_press_end(e: Event); fn on_force_press_peak(e: Event); fn on_force_press_start(e: Event); fn on_force_press_update(e: Event); fn on_horizontal_drag_cancel(e: Event); fn on_horizontal_drag_down(e: Event); fn on_horizontal_drag_end(e: Event); fn on_horizontal_drag_start(e: Event); fn on_horizontal_drag_update(e: Event); fn on_long_press(e: Event); fn on_long_press_end(e: Event); fn on_long_press_move_update(e: Event); fn on_long_press_start(e: Event); fn on_long_press_up(e: Event); fn on_pan_cancel(e: Event); fn on_pan_down(e: Event); fn on_pan_end(e: Event); fn on_pan_start(e: Event); fn on_pan_update(e: Event); fn on_scale_end(e: Event); fn on_scale_start(e: Event); fn on_scale_update(e: Event); fn on_secondary_tap_cancel(e: Event); fn on_secondary_tap_down(e: Event); fn on_secondary_tap_up(e: Event); fn on_vertical_drag_cancel(e: Event); fn on_vertical_drag_down(e: Event); fn on_vertical_drag_end(e: Event); fn on_vertical_drag_start(e: Event); fn on_vertical_drag_update(e: Event); } }
Are we still cool now?
Gesture in calling-elvis implements with typescript, but elvis still keeps these apis, so we still can rust the web happily with Elvis without confuse.
Layout
Follows MDN doc CSS Layout.
Components ๐ฆ
Elvis Layout mainly contains Flex and Grid, btw, Elvis offerrs two basic components Container and SizedBox for simple usages.
Container
/* Container */
import { Page, Elvis, Alignments, Color } from "calling-elvis";
const { SizedBox, Text } = Elvis;
// Generate a `Container`
let myContainer = Container(
Text("Where is my AJ-1?"), {
alignments: Alignments.Center,
height: 42,
margin: 10,
padding: 10,
width: 42,
color: Colors.Black,
backgroundColor: Colors.Red,
});
Page(mySizedBox).render();
The Alignments enum is from Flex, to be honest, Container component is a part of Flex family, but he is too brilliant to stay in Flex family, Layout calls him.
List
/* List */
import { Page, Elvis } from "calling-elvis";
const { List, Text } = Elvis;
// Generate a `List`
let myList = List(
Text("poor-orphan-1"),
Text("poor-orphan-2"),
Text("poor-orphan-3"),
);
Page(myList).render();
(Sorry about that), List is a set of poor orphan children, they don't have any style, just blowing in the wind.
SizedBox
/* SizedBox */
import { Page, Elvis } from "calling-elvis";
const { SizedBox, Text } = Elvis;
// Generate a `SizedBox`
let mySizedBox = SizedBox(
Text("My SizedBox"), {
height: 42,
width: 42,
});
Page(mySizedBox).render();
SizedBox just has width and height two arguments, we use this component to take some white space usually.
Flex
Follows MDN doc CSS Flexible Box Layout
Components ๐ฆ
The very basic core of Flex Layout components is called Flex, unfortunatly, it contains almost all css flex properties, not easy to use, that's why elvis composes some "newbie" components as well.
Align
/* Align */
import { Page, Elvis, Alignments } from "calling-elvis";
const { Align, Text } = Elvis;
// Generate an `Align`
let myAlign = Align(
Text("Align Elvis"), {
align: Alignments.Center,
});
Page(mySizedBox).render();
Align inherits the core usage of Alignment, it's quite simple, just one property.
Center
/* Center */
import { Page, Elvis, Alignments } from "calling-elvis";
const { Center, Text } = Elvis;
// Generate an `Center`
let myCenter = Center(
Text("My website only have a line of chars ๐ฆ "),
);
Page(mySizedBox).render();
Center is a very nice component, if your website only have a line of chars, use it!
Col
/* Col */
import { Page, Elvis, Alignments } from "calling-elvis";
const { Col, Text } = Elvis;
// Generate an `Col`
let myAlign = Col([
Text("All is above you all is sky"),
Text("All is behind you all is sea"),
]);
Page(mySizedBox).render();
Col towards column, the typical flow in html, with flexible enhance.
Flex
let flex = Flex(
List([
Text("hi, I'm the Lunatic Component's child No.1"),
Text("hi, I'm the Lunatic Component's child No.2"),
Text("hi, I'm the Lunatic Component's child No.3"),
)], {
basis: FlexBasis.Auto,
direction: FlexDirection.ColumnReverse,
grow: 1,
order: 1,
shrink: 2,
wrap: FlexWrap.Wrap,
});
This is the Lunatic Component to Ground Control, I'm stepping throw the Window.
Row
/* Row */
import { Page, Elvis, Alignments } from "calling-elvis";
const { Row, Text } = Elvis;
// Generate a `Row`
let myAlign = Row([
Text("I'm Wrong"),
Text("I'm Right"),
]);
Page(mySizedBox).render();
Both Col and Row are using flex-start, if you want to reverse the children of them, better to work on the list order.
Enums ๐ฉ
Elvis Layout Aligns follows the MDN doc [CSS Box Alignment][2], but simplify it into a enum Aligment here, Alignment is used by all Flex components and Container in Elvis.
Alignment
Here is the Alignment defination in rust source code.
#![allow(unused_variables)] fn main() { /// Magical Alignment pub enum Alignment { BottomCenter, BottomLeft, BottomRight, Center, CenterLeft, CenterRight, TopCenter, TopLeft, TopRight, } }
FlexBasis
#![allow(unused_variables)] fn main() { pub enum FlexBasis { Fill, MaxContent, MinContent, FitContent, Number(Unit), } }
Well, lunatic FlexBasis in Rust source code.
FlexDirection
#![allow(unused_variables)] fn main() { pub enum FlexDirection { Column, ColumnReverse, Row, RowReverse, } }
Lunatic FlexDirection, you know it.
Grid
Follows MDN doc Grids.
Here is the Grid section, Just let Elvis show you how Grid Grid.
Components ๐ฆ
Grid just have one component now, as you can see, Grid.
Grid
/* Grid */
import { Page, Elvis } from "calling-elvis";
const { Grid, Text, List } = Elvis;
// Generate an `Grid`
let myGrid = Grid(
List(
Text("Mercury"),
Text("Venus"),
Text("Earth"),
Text("Mars"),
Text("Jupiter"),
Text("Saturn"),
Text("Uranus"),
Text("Neptune"),
Text("Pluto"),
), {
autoRows: GridAutoRows.Auto(100),
// col: 3,
// gap: 2,
// row: 3,
// template: GridTemplate.Repeat(3, 1),
});
Page(mySizedBox).render();
Aah, duplicate name occurs, take it ease, only one Grid Component in Elvis too.
/* Grid */
import { Page, Elvis, Alignments } from "calling-elvis";
const { Grid, Text, List } = Elvis;
// Generate an `Grid`
let myGrid = Grid(
List(
Text("Mercury"),
Text("Venus"),
Text("Earth"),
Text("Mars"),
Text("Jupiter"),
Text("Saturn"),
Text("Uranus"),
Text("Neptune"),
Text("Pluto"),
),
);
Page(mySizedBox).render();
Grid is quite complex in some way, usually, we just Grid our contains.
Enums ๐ฉ
Grid Grid is hard to pronounce, most of time we don't need to do this.
GridAutoRows
#![allow(unused_variables)] fn main() { pub enum GridAutoRows { Auto(Unit, Option<Unit>), Fixed(Unit), } }
AutoRows affect the width of Grid children, and the Auto choice use the minmax function in css, if doesn't pass the second argument, it will be auto in meaning.
GridTemplate
#![allow(unused_variables)] fn main() { pub enum GridTemplate { Plain(Vec<Unit>), Repeat(i32, Unit), } }
In the Plain choice, Vec's length will be the column count of grid, and every Unit is the width of each column, Repeat just make this easier, every child are in the same width.
MultiColumn
Follows MDN doc Multiple-column layout.
The very easy way to layout our pages, maybe fantastic in the old web, it means, the true web, I like it.
Components ๐ฆ
Congratulations! one compoent got shot, again.
MultiColumn
/* MultiColumn */
import { Colors, Page, Elvis } from "calling-elvis";
const { Grid, Text, List } = Elvis;
// Generate an `Grid`
let myMultiColumn = MultiColumn(
List(
Text("Mercury"),
Text("Venus"),
Text("Earth"),
Text("Mars"),
Text("Jupiter"),
Text("Saturn"),
Text("Uranus"),
Text("Neptune"),
Text("Pluto"),
), {
color: Colors.red,
count: 3,
gap: 20,
style: MultiColumnStyle.Dotted,
});
Page(mySizedBox).render();
Homework, code a New York Times.
Enums ๐ฉ
Mainly the style of MultiColumn.
MultiColumnStyle
#![allow(unused_variables)] fn main() { pub enum MultiColumnStyle { None, Hidden, Dotted, Dashed, Solid, Double, Groove, Ridge, Inset, OutSet } }
If I were you, I will choose MultiColumnStyle.Groove, don't ask me why.
Router
/* Entry */
import { App } from "calling-elvis";
import { Africa, America, Asia, Europe, Oceania } from "./map";
class myApp extends App {
render() {
return App(
title: "Across the earth",
initialRoute: "/",
routes: {
"africa": Africa,
"america": America,
"asia": Asia,
"europe": Europe,
"Oceania": Oceania,
},
);
}
}
A ship have a Navigator, we call them App.
/* Router */
import { Elvis, Router, GestureDetector, Component } from "calling-elvis";
const { Center, Container, Text } = Elvis;
export default class myComponent extends Component {
render() {
return Page(
Center(
GestureDetector(
// onTap: () => Router.push("/start?target=Mars"),
onTap: () => Router.push(
"/stars",
arguments: {
target: "Mars",
},
),
),
Text("Lead me to the stars"),
),
);
}
}
We generate our routes in the entry of our Apps usually, Elvis has inner url parser in the navigator process, both url parameters and object style arguments are supported, so if we want to fly to the Mars, just do it.
Values
Values might be the most instresting part in Elvis, it focus on the design of our art.
Most of Elvis Components have enum properties, instead of writing them in plain text, it terms to 'less thinking', free as a bird, free as in freedom.
Homework
Paint out the Starry Starry Night, Shell we?
Color
Follows MDN doc CSS Color Value
#![allow(unused_variables)] fn main() { pub enum Colors { RGBA(i32, i32, i32, f64), Hex(String), HSL(Unit, i32, i32), Plain(String), Amber, AmberAccent, Black, Blue, BlueAccent, BlueGrey, Brown, Cyan, CyanAccent, DeepOrange, DeepOrangeAccent, DeepPurple, DeepPurpleAccent, Green, GreenAccent, Grey, Indigo, IndigoAccent, LightBlue, LightBlueAccent, LightGreen, LightGreenAccent, Lime, LimeAccent, Orange, OrangeAccent, Pink, PinkAccent, Purple, PurpleAccent, Red, RedAccent, Teal, TealAccent, Transparent, White, Yellow, YellowAccent, } }
So many colors...well, the Colors above is Elvis' color system.
Unit
Follows CSS Values 3 drafted in csswg.org.
#![allow(unused_variables)] fn main() { pub enum Unit { Ch(f64), Cm(f64), Dpi(f64), Dpcm(f64), Dppx(f64), Em(f64), Fr(f64), In(f64) Mm(f64), Pc(f64), Pt(f64), Px(f64), Q(f64), Rem(f64), Vh(f64), Vmax(f64), Vmin(f64), Vw(f64), } }
If we don't use Unit.X() in number field, Elvis will choose Unit.Px by default.
Absolute Lengths
| unit | name | equivalence |
|---|---|---|
| cm | centermeters | 1cm = 96px/2.54 |
| mm | millimeters | 1mm == 1/10th of 1cm |
| Q | quarter-millimeters | 1Q = 1/40th of 1cm |
| in | inches | 1in = 2.54cm = 96px |
| pc | picas | 1pc = 1/6th of 1in |
| pt | points | 1pt = 1/72th of 1in |
| px | pixels | 1px = 1/96th of 1in |
Relative Lengths
| unit | relative to |
|---|---|
| em | font size of element |
| ex | x-height of element's font |
| ch | width of the "0" (ZERO, U+0030) glyph in the elementโs font |
| rem | font size of the root element |
| vw | 1% of viewportโs width |
| vh | 1% of viewportโs height |
| vmin | 1% of viewportโs smaller dimension |
| vmax | 1% of viewportโs larger dimension |
Others
| unit | represents |
|---|---|
| dpi | Dots per inch |
| dpcm | Dots per centmeter |
| dppx | Dots per px unit |
| fr | This unit represents one fraction of the available space in the grid container. |