RoadMap

PlanDate
Virtual-DOM2020.1.28 - 2020.2.1
Basic-Component2020.2.5 - 2020.2.7
Router2020.2.7 - 2020.2.10
Native Components2020.2.10 - 2020.2.11
Benchmark2020.2.11 - 2020.2.15
Beta Versin2020.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

unitnameequivalence
cmcentermeters1cm = 96px/2.54
mmmillimeters1mm == 1/10th of 1cm
Qquarter-millimeters1Q = 1/40th of 1cm
ininches1in = 2.54cm = 96px
pcpicas1pc = 1/6th of 1in
ptpoints1pt = 1/72th of 1in
pxpixels1px = 1/96th of 1in

Relative Lengths

unitrelative to
emfont size of element
exx-height of element's font
chwidth of the "0" (ZERO, U+0030) glyph in the elementโ€™s font
remfont size of the root element
vw1% of viewportโ€™s width
vh1% of viewportโ€™s height
vmin1% of viewportโ€™s smaller dimension
vmax1% of viewportโ€™s larger dimension

Others

unitrepresents
dpiDots per inch
dpcmDots per centmeter
dppxDots per px unit
frThis unit represents one fraction of the available space in the grid container.