jaspr icon indicating copy to clipboard operation
jaspr copied to clipboard

Add utility methods / components for common html elements

Open schultek opened this issue 2 years ago • 6 comments

Discussion related to #13

Status quo

The current way of defining html elements is by using DomComponent and providing a tag. This leaves the most freedom to developers, but can get verbose quickly when composing uis:

This:

return DomComponent(
  tag: 'div',
  children: [
    DomComponent(
      tag: 'h1', 
      child: Text('This is a title'),
    ),
    DomComponent(
      tag: 'p',
      children: [
         Text('Hello '),
         DomComponent(
           tag: 'b',
           child: Text('World!'),
        ),
      ],
    ),
  ],
);

is unarguably a lot more verbose than standard html:

<div>
  <h1>This is a title</h1>
  <p>Hello <b>World!</b></p>
</div>

Proposal / Discussion

While DomComponent should stay as the low-level base for all html elements, we need a more simple way to use common html elements like div and others. This should be added on top of the core framework and be optional to use by the developer.

What should such an element look like?

There are two ways we could define such elements:

  1. As their own StatelessComponent
  2. As methods returning a DomComponent

Separate components (1) would define a class for each element, take any arguments in the constructor and return a DomComponent in the build() method. Since classes use PascalCase for their naming, we either

  • (a) capitalize the tag name (Div, P, H1), or
  • (b) extend the tags to their more descriptive names (Division, Paragraph, Heading1).

Both are not really ideal, since (a) would have a bunch of single-letter class names and (b) would be again quite verbose for a lot of elements. Additionally, having separate components introduces a lot of extra depth into the component tree when building uis (2 components for each element, the elements component + the dom component), which will have some performance implications for larger apps.

Element methods (2) would provide a way to define shorthands for common elements without the overhead of additional components. A method can also have custom properties specific for the element and would just return a single DomComponent with the static tag and provided attributes. Methods also have lower-case names and could directly mirror the original html tags (div(), p(), h1()), keeping the code concise and appearing similar to original html.

Which elements are included?

Of course we want as many elements included as possible, however this might be a challenge as there are a lot of different elements in the html spec.

When writing this manually we would have to start with a few most common elements and expand the list over time. However this would also mean a lot of maintenance when the component api or properties of DomComponent change (which might happen since jaspr is very early stage).

A better and more easy way would be to generate the methods and their bodies for each html element automatically. A good example would be domino_html which does exactly this for the domino package. It uses a script that crawls developer.mozilla.org/en-US/docs/Web/HTML/Element and developer.mozilla.org/en-US/docs/Web/HTML/Attributes to get all html elements and their attributes. Modifying this script to output jaspr-compatible code should be feasible. There are however some additional considerations to make:

  • Some of the element have many uncommon attributes. These get very verbose and make the parameter list unclear and convoluted. A possibility would be to not include global attributes as defined by the attributes page.
  • Some attributes have custom types other than String (mainly boolean, int or enums). It would be cool to provide them as typed attributes (especially the enums), but I don't know right now how to get this information, since it is not specified in the attributes page.

schultek avatar Jul 13 '22 18:07 schultek

I found that the content source for mdn is also here: https://github.com/mdn/content/tree/main/files/en-us/web/html/element Not really structured data, but at least markdown files for each element, so parsing this might be easier.

schultek avatar Jul 13 '22 19:07 schultek

@schultek Did you see my code here: https://github.com/schultek/jaspr/pull/13 how I implemented it there? I define new elements as class inherited from StatelessComponent which have some more extra parameters depends on the specific element but in result you are still building only one DomComponent so you don't have a lot of extra depth into the component tree.

I am for naming in more descriptive names. I don't like any very short names of classes or functions/methods so P, H1, a, b are too short. Minimal length 3 characters are good for me yet so Div have really minimal length. Divider is a little better.

I don't like also idea about methods returning DomComponent. I would like to keep this framework in more Flutter like style then domino_html style so new elements should be classes inherited from StatelessComponent I think.

Also I don't think that we should mirror original html tags. I would like create web framework for developers which want to create web pages in Dart but doesn't have any HTML/CSS knowledge. I would like create framework which will be similar or same simple like Flutter with very good community and great documentation/tutorials. Jaspr should not have HTML/CSS knowledge as a requirements for development with it.

So instead of: a, h1, div, img, br, hr, tr, td, etc.. I would like use: Link, Header, Container, Image, Table, Row, Cell, etc.. and instead of styling elements: i, b, em, strong we should use CSS styles. And we really cannot implement absolutely all html elements with all their attributes.

I want to keep it really simple how we can and implement only elements/tags which we really need.

Here is my list of the most frequently HTML elements and their CSS styles which we should implement and others (for ex. iframe) we can implement later if it will be really important.

mjablecnik avatar Jul 13 '22 21:07 mjablecnik

I looked at the pr in detail, but just wanted to take a step back and discuss what's the best way to implement this.

I get your point and agree that we should make things simple + familiar for flutter devs. But I also want the framework to be appealing to web devs, and as a web dev I would want to have the actual bare-bone html elements. Thinking about it, targeting both with a single set of components might be difficult to impossible.

Let's take the Link component for example. For flutter devs this might be good and easier to understand than maybe an Anchor component (a tag). However for web devs this is confusing because there is also the link tag which then wouldn't have its own component.

So then we would have to have two sets of components

  • bare-bone html elements for web devs (a, h1, div, etc...)
  • simple flutter-like components (Link, Header, Container, Row, etc...)

So the question is if we have to / want to decide on one and leave the others out, or implement both for the developer to choose (e.g. under a separate import like package:jaspr/html.dart vs package:jaspr/components.dart)

schultek avatar Jul 14 '22 07:07 schultek

Yes OK, I understand. For me as non web developer I don't like tags like a, h1, div, etc.. for me are not readable and understandable a lot. Hence I didn't work with HTML/CSS a lot in past.. ;-) But I understand your view and I agree with you.

So let's go create two separate libraries with imports package:jaspr/html.dart and package:jaspr/components.dart. You can develop on bare-bone html elements which will be methods returning DomComponent. I will develop on Flutter-like elements which will be usable for non web developers.

And developers can choose which will better for them. :-)

mjablecnik avatar Jul 14 '22 08:07 mjablecnik

Sounds like a plan. 👍

schultek avatar Jul 14 '22 09:07 schultek

So when I will have a time, I will change package:jaspr/ui.dart to package:jaspr/components.dart in my PR.

mjablecnik avatar Jul 14 '22 09:07 mjablecnik