arrow-left

All pages
gitbookPowered by GitBook
1 of 10

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Deprecated

MagicForm | <magic-form>

captures the submit event of the nested form and sends it to MagicFormProvider

hashtag
Examples

<MagicForm>
  <form action="user
<MagicForm onChangeState={event=>{
  console.

hashtag
Events

Type
Description
Config

hashtag
Properties

Property
Description
Type

hashtag
Property.state

"
>
<input type="text" name="name" />
<input type="text" name="email" />
<button>Create user</button>
</form>
</MagicForm>
log
(
event
.
currentTarget
.
state
);
}}>
<form action="user">
<input type="text" name="name" />
<input type="text" name="email" />
<button>Create user</button>
</form>
</MagicForm>

"ChangeState"

Dispatched when status changes from provider

{bubbles: false, composed: false}

state

Read only, Current status submission of the form

Object

action

Defines the action to dispatch, if not defined it can be inherited from the

tag

String

interface MagicFormActionStatus{
    result?:any,
    timestamp?: number,
    status: "pending" | "fulfilled" | "rejected"
}

@atomico/magic-form

Improves the form development experience thanks to the use of webcomponents to centralize submission

A powerful form submission manager created with Atomicojs webcomponents, with MagicForm you can:

  1. Simplify the sending of forms by centralizing the sending actions through a provider.

  2. Know at all times the forms that are being provided and show their status.

  3. Isolate a group of actions according to provider, this means that if a provider does not register the action it will bubble to the parent provider.

hashtag
Installation and import

hashtag
Usage

magic-form-provider captures all the forms nested in magic-form when executing the submit event by the form and distributes them according to the definition of the action attribute to each method of the actions object

hashtag
API

MagicForm Hooks

hashtag
useMagicForm

catch the submit event and send it to the useMagicFormProvider

hashtag

@atomico/design-tokens api

hashtag
module

hashtag
compose

create a pipeline of functions that share the same CSSStyleSheet.

MagicForm in Microfrontend

MagicForm will help you manage your applications with microfrontend architecture, through the concept of isolated providers, for example:

Any action not registered by the provider will continue to bubble until the next provider.

MagicForm in React and Preact

MagicForm api is equivalent in Atomicojs, React and Preact at JSX level

hashtag
Installation

hashtag
Usage

@atomico/design-tokens

Accelerates the implementation of design tokens in web components in a sustainable and scalable way

With @atomico/design-tokens you can:

MagicFormProvider | <magic-form-provider>

receives the submits from MagicForm

hashtag
Example

hashtag
Events

<MagicFormProvider actions={{
    async login(){...}
}}>
    <MagicFormProvider actions={actionsClient}>
        <PageClient/>
    </MagicFormProvider>
    <MagicFormProvider actions={actionsAdmin}>
        <PageAdmin/>
    </MagicFormProvider>
</MagicFormProvider>
import { MagicForm, MagicFormProvider } from "@atomico/magic-form/react";

export function App() {
  return (
    <>
      <MagicFormProvider
        actions={{
          async add(form) {
            return fetch("./my-api", {
              method: "post",
              body: new FormData(form),
            }).then((res) => res.json());
          },
        }}
      >
        <MagicForm>
          <form action="user">
            <input type="text" name="name" />
            <input type="text" name="email" />
            <button>Create user</button>
          </form>
        </MagicForm>
      </MagicFormProvider>
    </>
  );
}
Type
Description
Config

"ChangeForms"

Dispatched when forms changes from provider

{bubbles: false, composed: false}

hashtag
Properties

Property
Description

forms

Read only, current state of the captured forms

actions

object that defines the actions to be captured by the MagicFormProvider

hashtag

useMagicFormProvider

receives the submits from useMagicForm

import { useRef } from "atomico";
import { useMagicForm } from "@atomico/magic-form/hooks";

function component() {
  const ref = useRef();
  const [state, submit] = useMagicForm(ref);
  return (
    <host>
      <form ref={ref} action="addUser">
        <input type="text" name="name" />
        <input type="email" name="email" />
        <button>Add user</button>
      </form>
    </host>
  );
}
import { useHost } from "atomico";
import { useMagicFormProvider } from "@atomico/magic-form/hooks";

function component() {
  const ref = useHost();
  const forms = useMagicFormProvider(ref, {
    addUser(form) {
      const body = new FormData(form);
      return fetch(formData, { method: "POST", body }).res((res) => res.json());
    },
  });
  return <host></host>;
}

hashtag
Syntax

hashtag
tokens

transform an object to custom properties.

hashtag
Syntax

hashtag
Example 1

hashtag
Example 2, variations

hashtag
classes

It facilitates the reuse of tokens, through the generation of dynamic classes.

hashtag
Syntax

hashtag
Example

import { MagicForm, MagicFormProvider } from "@atomico/magic-form/preact";

export function App() {
  return (
    <>
      <MagicFormProvider
        actions={{
          async add(form) {
            return fetch("./my-api", {
              method: "post",
              body: new FormData(form),
            }).then((res) => res.json());
          },
        }}
      >
        <MagicForm>
          <form action="user">
            <input type="text" name="name" />
            <input type="text" name="email" />
            <button>Create user</button>
          </form>
        </MagicForm>
      </MagicFormProvider>
    </>
  );
}
npm i @atomico/magic-form @atomico/react
import { MagicForm, MagicFormProvider } from "@atomico/magic-form";

function component() {
  return (
    <host>
      <MagicFormProvider 
        actions={{
          async add(form) {
            return fetch("./my-api", {
              method: "post",
              body: new FormData(form),
            }).then((res) => res.json());
          },
        }}
      >
        <MagicForm>
          <form action="user">
            <input type="text" name="name" />
            <input type="text" name="email" />
            <button>Create user</button>
          </form>
        </MagicForm>
      </MagicFormProvider>
    </host>
  );
}
compose(
    tokens(
        {
            size: {
                xl: "32px",
                l: "28px",
                m: "24px",
            }
        },
        "ds"
    )
);
:host{
    --size-xl: var( --ds--size-xl,  32px);
    --size-l: var( --ds--size-xl,  28px);
    --size-m: var( --ds--size-xl,  24px);
}
compose(
    tokens(
        {
            size: {
                xl: "32px",
                l: "28px",
                m: "24px",
            },
            variation: {
                small: {
                    size: {
                        xl: "28px",
                        l: "24px",
                        m: "20px",
                    },                
                }
            }
        },
        "ds"
    )
);
:host{
    --size-xl: var( --ds--size-xl,  32px);
    --size-l: var( --ds--size-xl,  28px);
    --size-m: var( --ds--size-xl,  24px);
}
:host([small]){
    --size-xl: var( --ds-small--size-xl,  28px);
    --size-l: var( --ds-small--size-xl,  24px);
    --size-m: var( --ds-small--size-xl,  20px);
}
import {css} from "atomico";
import { compose, classes } from "@atomico/design-tokens";

const designTokens = compose(
    classes(
        {
            size: {
                xl: "32px",
                l: "28px",
                m: "24px",
            }
        }
    )
);

export const classUtils = designTokens(
    css`
        .gap.--size{
            gap: var(--size);
        }
    `
);
.gap\.xl{    
    gap: var(--size-xl);
}
.gap\.l{    
    gap: var(--size-l);
}
.gap\.m{    
    gap: var(--size-m);
}
import {
    compose,
    tokens,
    classes
} from "@atomico/design-tokens";
compose(
    ...middleware: ((sheet: CSSStyleSheet, lastParam:any)=> any)[]
): (firstParam:any)=>sheet: CSSStyleSheet;
tokens(theme: Tokens, prefix: string);
classes(tokens:Tokens);
hashtag
Resolve scalability and maintenance issues with your design tokens.

Design systems are complex to develop, due to the number of configurations that are shared between all our components, with @atomico/design-tokens we will solve:

  1. Naming problems of the custom properties of your design tokens.

  2. Legibility of your CSS.

hashtag
How does @atomico/design-tokens solve the scalability of your design tokens?

For this example we will use Atomico, by the way you can use @atomico/design-tokens with any library.

The result of the CSS will be the following:

This is a technique that I have created to improve the scalability of design tokens, with it you can:

  1. edit token globally using custom properties:

This is also applicable within a selector.

  1. Simplify maintenance, since your components will use the custom properties without a prefix:

hashtag
Create utility classes to be used internally by your component system.

I am personally a fan of custom properties, but their use would become repetitive, to avoid this and improve maintenance @atomico/design-tokens introduces classes, a generator of utility classes based on the proposed design tokens, example:

The classes middleware will parse the CSSStyleSheet to relate the custom propeprtiy --size as a class of .gap, internally the css will be as follows:

This makes it really simple to reuse the tokens, example:

Resolve scalability and maintenance issues with your design tokens.
Create utility classes from design tokens.
@atomico/design-tokens apichevron-right
MagicFormProvider | <magic-form-provider>chevron-right
MagicForm | <magic-form>chevron-right
MagicForm Hookschevron-right
MagicForm in React and Preactchevron-right

MagicForm Patterns

MagicForm makes it easy to send data to the server, here are some useful patterns

hashtag
Login

async login(form: HTMLFormElement){
  const user = Object.fromEntries(new FormData(form) as any);
  
  return myApi.login(user);
}

hashtag
Create data

async create(form: HTMLFormElement){
  const result = await fetch("/my-api",{method: "post",body: new FormData(form)});
  return result.json();
}

hashtag
Search

The following action allows us to generate searches from a form tag that has an input search, the objective is to consume the search value recursively only if there has been a change in the input search during the fetch performed.

import { css } from "atomico";
import { compose, tokens } from "@atomico/design-tokens";

const designTokens = compose(
  tokens(
    {
      size: {
        xl: "40px",
        l: "32px",
        m: "28px",
        s: "20px",
      },
    },
    "ds"
  )
);

export const tokens = designTokens(css``);
:host {
  --size-xl: var(--ds--size-xl, 40px);
  --size-l: var(--ds--size-l, 32px);
  --size-m: var(--ds--size-m, 28px);
  --size-s: var(--ds--size-s, 20px);
}
:root {
  --my-ds-size-xl: 50px;
}
import { c, css } from "atomico";
import tokens from "./tokens";
function button() {
  return (
    <host shadowDom>
      <slot />
    </host>
  );
}

button.styles = [
  tokens,
  css`
    :host {
      height: var(--size-xl);
      background: var(--color-primary-60);
      padding: var(--size-xxs) var(--size-xs);
    }
  `,
];
import { css } from "atomico";
import { compose, tokens, classes } from "@atomico/design-tokens";

const designTokens = compose(
  classes({
    size: {
      xl: "40px",
      l: "32px",
      m: "28px",
      s: "20px",
    },
  })
);

export const tokensSize = designTokens(
  css`
    .gap.--size {
      gap: var(--size);
    }
  `
);
.gap\.xl {
  gap: var(--size-xl);
}

.gap\.l {
  gap: var(--size-l);
}

.gap\.m {
  gap: var(--size-m);
}

.gap\.s {
  gap: var(--size-s);
}
import { c } from "atomico";
import { tokensSize } from "./tokens";

function button() {
  return (
    <host shadowDom>
      <button class="gap.xl">
        <slot />
      </button>
    </host>
  );
}

button.styles = tokensSize;

customElements.define("my-button", c(button));
npm install @atomico/magic-form
import { 
    MagicForm,
    MagicFormProvider 
} from "@atomico/magic-form";
import { 
    useMagicForm, 
    useMagicFormProvider 
} from "@atomico/magic-form/hooks";
<magic-form-provider>
  <magic-form>
    <form action="user">
      <input type="text" name="name" />
      <input type="text" name="email" />
      <button>Create user</button>
    </form>
  </magic-form>
</magic-form-provider>
<script>
  document.querySelector("magic-form-provider").actions = {
    async add(form) {
      return fetch("./my-api", {
        method: "post",
        body: new FormData(form),
      }).then((res) => res.json());
    },
  };
</script>
import { MagicForm, MagicFormProvider } from "@atomico/magic-form";

function component() {
  return (
    <host>
      <MagicFormProvider 
        actions={{
          async add(form) {
            return fetch("./my-api", {
              method: "post",
              body: new FormData(form),
            }).then((res) => res.json());
          },
        }}
      >
        <MagicForm>
          <form action="user">
            <input type="text" name="name" />
            <input type="text" name="email" />
            <button>Create user</button>
          </form>
        </MagicForm>
      </MagicFormProvider>
    </host>
  );
}
async search(form: HTMLFormElement) {
  const beforeFetch = Object.fromEntries(new FormData(form) as any);
  
  await new Promise((resolve) => setTimeout(resolve, 1000)); // debounce
  
  const result = await fetch(beforeFetch.search);
  
  const afterFetch = Object.fromEntries(new FormData(form) as any);
  
  if (afterFetch.search === beforeFetch.search) {
    return result;
  } else {
    return login(form);
  }
}
GitHub - atomicojs/magic-formGitHubchevron-right
Logo
GitHub - atomicojs/design-tokensGitHubchevron-right
Logo