Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
MagicForm will help you manage your applications with microfrontend architecture, through the concept of isolated providers, for example:
<MagicFormProvider actions={{
async login(){...}
}}>
<MagicFormProvider actions={actionsClient}>
<PageClient/>
</MagicFormProvider>
<MagicFormProvider actions={actionsAdmin}>
<PageAdmin/>
</MagicFormProvider>
</MagicFormProvider>Any action not registered by the provider will continue to bubble until the next provider.
catch the submit event and send it to the useMagicFormProvider
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>
);
}receives the submits from useMagicForm
captures the submit event of the nested form and sends it to MagicFormProvider
<MagicForm>
<form action="user">
<input type="text" name="name" />
<input type="text" name="email" />
<MagicForm onChangeState={event=>{
console.log(event.currentTarget.state);
}}>
<form action="user">
<input type="text" name
MagicForm makes it easy to send data to the server, here are some useful patterns
async login(form: HTMLFormElement){
const user = Object.fromEntries(new FormData(form) as any);
return myApi.login(user);
}async create(form: HTMLFormElement){
const result = await fetch("/my-api",{method: "post",body: new FormData(form)});
return result.json();
}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.
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:
Simplify the sending of forms by centralizing the sending actions through a provider.
Know at all times the forms that are being provided and show their status.
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.
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
Accelerates the implementation of design tokens in web components in a sustainable and scalable way
With @atomico/design-tokens you can:
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:
Naming problems of the custom properties of your design tokens.
Legibility of your CSS.
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:
edit token globally using custom properties:
This is also applicable within a selector.
Simplify maintenance, since your components will use the custom properties without a prefix:
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:
npm i @atomico/magic-form @atomico/reactimport { 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>
);
}import {
compose,
tokens,
classes
} from "@atomico/design-tokens";"ChangeForms"
Dispatched when forms changes from provider
{bubbles: false, composed: false}
forms
Read only, current state of the captured forms
actions
object that defines the actions to be captured by the MagicFormProvider
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>;
}"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
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);
}
}interface MagicFormActionStatus{
result?:any,
timestamp?: number,
status: "pending" | "fulfilled" | "rejected"
}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>
</>
);
}
compose(
...middleware: ((sheet: CSSStyleSheet, lastParam:any)=> any)[]
): (firstParam:any)=>sheet: CSSStyleSheet;tokens(theme: Tokens, prefix: string);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);
}classes(tokens:Tokens);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);
}npm install @atomico/magic-formimport {
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>
);
}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));