Skip to content

Instantly share code, notes, and snippets.

@msaraiva
Last active May 19, 2021 19:31
Show Gist options
  • Save msaraiva/dd40ab537e1508f1af2954a19faa2d54 to your computer and use it in GitHub Desktop.
Save msaraiva/dd40ab537e1508f1af2954a19faa2d54 to your computer and use it in GitHub Desktop.
Proposal for syntax changes in Surface v0.5

Proposal

Templates

Shorthand notation for assigning slots.

Usage

<Card>
  <:header>
    ...
  </:header>
  <:footer>
    ...
  </:footer>
</Card>

instead of:

<Card>
  <template slot="header">
    ...
  </template>
  <template slot="footer">
    ...
  </template>
</Card>

Impact

  • deprecate <template> usage, then remove it in next minor version
  • fixable with converter

Status

Approved ✅

Root property

Introduce the ability to define a prop that can be passed without the attibute name.

Usage

By defining a prop as root: true, for instance:

prop condition, :boolean, root: true

you can use:

<MyComp {@var > 10}>
  ...
</MyComp>

instead of:

<MyComp condition={@var > 10}>
  ...
</MyComp>

Note: Only one prop can be defined as root.

Impact

  • no breaking change as it's an addition

Status

Approved ✅

Function Components (same syntax of new LV template language)

Allow injecting the new "function component" that will be introduced in Phoenix.

Usage

Local/imported functions:

<.my_func>
  ...
</.my_func>

Remote functions:

<Components.SomeComponent.some_func>
  ...
</Components.SomeComponent.some_func>

Impact

  • fixable with converter
  • although we'll accept this syntax, we'll keep the abstraction of Surface.Component as a wrapper for stateless components using the new component/3 function. This means there will be no change in the syntax if the user wants to keep the implementation in a module. However, there will be no mount callback for now (maybe we can keep update), so we need to check which stateless components depend on those callbacks and adapt them.

Status

Approved ✅

Replace {{ }} with { }

Make the syntax less verbose and allow some minimal compatibility with the new LV template language.

Usage

<div class={@class}>

Impact

  • breaking change
  • fixable with converter

Status

Approved ✅

If/elseif/else

Support if...elseif...else logic as built-in constructs instead of components.

Usage

{#if @var > 10}
  ...
{#elseif @var < 10}
  ...
{#else}
  ...
{/if}

Impact

  • deprecate <If> usage, then remove it in next minor version
  • fixable with converter

Status

Approved ✅

Case

Support case logic as built-in constructs.

Usage

{#case @var}
  {#match [first | _ ]}
    ...
  {#match _}
    ...
{/case}

Impact

  • no breaking change as it's an addition

Status

Approved ✅

Dynamic attributes/properties

Shorthand notation for passing dynamic attributes. It replaces both, :attrs and :props.

Usage

<div class="panel" {...@attrs}>

Note 1: The new Liveview template will introduce a new way to pass dynamic attributes. Probably:

<div class="panel" {@attrs}>

If we use the same approach, it will conflict with the proposed "root property", which invalidate the way we can build our built-in constructs. So I think it's better to have a React-like approach using {...attrs} instead.

Impact

  • deprecate :attrs and :props, then remove them in next minor version
  • fixable with converter

Status

Approved ✅

Strict attribute values

The new syntax will accept only literal strings or expressions as attribute values. There will be no auto-conversion of literals nor evaluation of embedded expressions like id="id_{{@id}}".

See discussion surface-ui/surface#319 (comment) for details.

Usage

Basically, the same but more strict when accepting literals.

Boolean: <div selected={true}> instead of <div selected=true> Integers: <div tabindex={1}> instead of <div tabindex=1> Atoms: <div some_atom_attr={:an_atom}> instead of <div some_atom_attr="an_atom"> Embedded expressions: id={"id_#{@id}"} instead of id="id_{{@id}}"

Impact

  • breaking change (deprecation possible for some cases)
  • fixable with converter (except the atom case)

Status

Approved ✅

Directives

Add a prefix s-* for directives to avoid conflicts with frontend libs like Alpine.js. The : notation will be kept as a shorthand for s-. The user can then choose which notation to use depending on the frontend.

Usage

<div s-on-click="click">

shorthand (the current notation)

<div :on-click="click">

Impact

  • no breaking change as it's an addition

Status

Approved ✅

Tagged expressions

Allow = and ~ as built-in expression markers. Other markers can be customized by the user, namely $ and %. We may include other symbols in the future as long as they are not a valid tokens at the begining of an elixir expression.

Usage

The = marker would allow users to write:

<button
  {=@type}
  {=@aria_label}
  {=@disabled}
  {=@value}
  ...
</button>

as a shorthand for:

<button
  type={@type}
  aria-label={@aria_label}
  disabled={@disabled}
  value={@value}
  ...
</button>

This is similar to Svelte's <button {@type}> syntax, but with explicit = marker. We need to use a marker, othewise, it would conflic with the "root property" feature.

The ~ marker would allow users to write:

<h2>{~ "Welcome!"}</h2>

as a shorthand for:

<h2>{gettext "Welcome!"}</h2>

If we allow custom implemenations for tagged expressions, the user would be able to map those markers to specific formatter functions. Examples:

The $ marker would allow users to write:

<h2>{$ @total}</h2>

as a shorthand for:

<h2>{format_currency(@total)}</h2>

The % marker would allow users to write:

<h2>{% @updated_at}</h2>

as a shorthand for:

<h2>{format_date(@updated_at)}</h2>

Allowing users to map markers to their own functions brings the same tradeoffs we have when overriding standard operators in general like + or *. It can be practical depending or the use case but can lead to more obscure code if the user maps those markers to something that might not be expected by other developers not familiar with the codebase.

Impact

  • no breaking change as it's an addition
  • require adjustments for the syntax highlighters

Status

Waiting for approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment