Atomico a micro-library for creating webcomponents using only functions, hooks and virtual-dom.
MIT License
Bot releases are visible (Hide)
Published by UpperCod about 1 year ago
serialize
function from the atomico/utils
module when used from SSR.template
function in the SSR (Server-Side Rendering) of the atomico
module.Published by UpperCod about 1 year ago
The dispatchEvent function is intended to facilitate component testing, allowing you to dispatch events from an event and customize its target, example:
import { fixture, asyncEventListener, dispatchEvent } from "atomico/test-dom";
test("check target", async () => {
const myComponent = fixture(<MyComponent />);
const eventName = "myEvent";
queueMicrotask(() => {
const target = { value: 1000 };
const event = new Event(eventName);
dispatchEvent(myComponent, event, target);
});
const event = await asyncEventListener(myComponent, eventName);
expect(event.target).toEqual({ value: 1000 });
});
Published by UpperCod about 1 year ago
List of new features
null | undefined
With Atomico, you can now create your own types, enabling you to, for example:
const TypeImport = createType((value: string | Promise<any>) =>
typeof value === 'string' ? import(value) : value
);
The above TypeImport type will always give you a value derived from dynamic imports.
interface User {
id: number;
nickname: string;
role: 'admin' | 'client';
}
const TypeUser = createType(
(value: User | string): User =>
typeof value === 'string' ? JSON.parse(value) : value,
(value) => value.role
);
The TypeUser type above allows receiving inputs as either an object or a string and always returns a User object. When reflected as an attribute, it will reflect the role property of the user object.
When Atomico receives an attribute, it parses its value according to the type. However, this analysis only covers types like String, Boolean, Number, Object, and Array. Now, Atomico allows transforming a string value into the associated type using the 'new' operator. Example:
component.props = {
startDate: Date,
};
The above declaration used to result in an error since a string is not of type Date. Now, Atomico internally instantiates the Date type, using the input value as an argument.
Previously, the 'value' defined only the initial value of the component's prop, but now it sets the default value. Example:
function myComponent({ theme }: Props<typeof myComponent>) {
return <host shadowDom>{theme}</host>;
}
myComponent.props = {
theme: {
type: String,
value: (): 'primary' | 'secondary' => 'primary',
},
};
const MyComponent = c(myComponent);
Now, if we set <MyComponent theme={null}/>
, it will be changed to primary
.
This also implies that when inferring props, they come as both existing and optional at the function level. Example:
Before
function myComponent(props: Props<typeof myComponent>) {
const double = props?.value ? props?.value * 2 : 0;
return <host>{double}</host>;
}
myComponent.props = {
value: { type: Number, value: 0 },
};
Now at [email protected]
function myComponent(props: Props<typeof myComponent>) {
const double = props.value * 2;
return <host>{double}</host>;
}
myComponent.props = {
value: { type: Number, value: 0 },
};
Atomico now introduces a new hook called useAbortController, which allows aborting the execution of asynchronous calls, example:
import { useAsync, useAbortController } from 'atomico';
async function getUser(id: number, signal: AbortSignal) {
return fetch(`/id/${id}`, { signal });
}
function myComponent({ userId }: Props<typeof myComponent>) {
const { signal } = useAbortController([userId]);
const user = useAsync(getUser, [userId, signal]);
return <host>{user.name}</host>;
}
myComponent.props = { userId: { type: Number, value: 0 } };
The significant advantage of using useAbortController is the automatic cancellation of asynchronous processes that depend on it when modifying the arguments provided to useAbortController (similar to useEffect).
At times, when using auto-import, VS Code might infer atomico/core
as a valid path. This has been removed to enhance code editor auto-completion.
Published by UpperCod over 1 year ago
AtomicoElement
Allows you to identify constructors created with Atomico at the typescript level, example:
type IsAtomicoElement = typeof MyElement extends AtomicoElement ? 1 : 0;
Props
Now the props type can be used with the satifields operator, example:
const props = {
range: Number,
next: {
type: String,
event: {
type: "demo",
},
},
} satisfies Props;
Published by UpperCod over 1 year ago
Sometimes when generating an update from SSR the event system of the props is dispatched, this version omits the dispatch of events in SSR
Published by UpperCod over 1 year ago
Published by UpperCod over 1 year ago
This change is intended to give consistency between the name of the function and the name of the class returned by the c
function, example:
function myComponent(){}
c( myComponent ).name; // MyComponent
When defining the custom Element as a class, the name will be taken from the functional webcomponent and it will be used as a class name in camel case format.
Published by UpperCod almost 2 years ago
For errors due to transformation of serializable values, Atomico now adds a wrapper to improve the inspection of the origin of the error, example:
With this you can easily identify which is the target (webcomponent) that generates the error due to a wrong attribute setting.
Added path jsx-dev-runtime
as an alias for path jsx-runtime
.
Published by UpperCod almost 2 years ago
This PR refactors part of the Atomico code, to introduce new features and maintenance improvements.
useInsertionEffect
hook.useAsync
and useSuspense
.useId
, con soporte tanto al usar SSR o Browser.copy of React's useInsertionEffect, similar to useEffect, but this hook is executed before rendering the DOM, this hook is useful for 3rd party apis looking to manipulate the DOM before rendering the component's DOM.
copy of React's useId, creates a unique ID for the Atomico context, the generated ID will depend on the origin, whether it is SSR or Client.
SSR ID Example: s0-1
.
CLIENT ID Example: c0-1
.
useAsync: this hook is a step before the adoption of React's use
hook, it allows to suspend the rendering execution of the webcomponent and to communicate to useSuspense
the execution suspension.
const getUser = (id: number): Promise<{ name: string }> =>
fetch(`user/${id}`).then((res) => res.json());
function component({ id }) {
const user = useAsync(getUser, [id]);
return <host>{user.name}</host>;
}
Nota 1: This hook conditions the execution of the promise according to a list of optional arguments, which allows the promise to be regenerated every time the list changes.
Nota 2: useAsync
suspends rendering execution and resumes it only if the promise has been resolved or rejected.
useSuspense: captures all nested executions that have been paused, it returns an object that defines the state of the executions.
function component() {
const status = useSuspense();
return (
<host shadowDom>
{status.pending ? "Loading..." : status.fulfilled ? "Done!" : "Error!"}~
<slot />
</host>
);
}
This new change has generated modifications to the core at the level of hooks, currently the changes have passed all the tests without complications, we will be attentive to any bug or unexpected behavior.
For creators of third party APIs based on createHooks, cleanEffects now has 3 execution steps:
useInsertionEffects
.useLayoutEffects
.useEffect
.hooks.cleanEffects()()();
Published by UpperCod almost 2 years ago
This hook allows us to observe the resolution of a promise, with this hook we seek to provide a utility at the core level to work with asynchronous tasks
import { usePromise } from "atomico";
const callback = (id: number)=>Promise.resolve(id);
const args:Parameters<typeof callback> = [1];
const autorun = true;
const promise = usePromise( callback, args, autorun );
where:
callback
: asynchronous function.args
: arguments that callback requires.autorun
: by default true, it automatically executes the promise, if it is false the execution of the promise is suspended.promise
: return object, at the type level it defines the following properties:
pending
: boolean, defines if the promise is executed but pending resolution.fulfilled
: boolean , defines if the promise has been fulfilledresult
: result of the promise.rejected
: boolean, defines if the promise has been rejected.import { usePromise } from "atomico";
const getUsers = (id: number) => Promise.resolve({ id, name: "Atomico" });
function component() {
const promise = usePromise(getUsers, [1]);
return (
<host>
{promise.fulfilled ? (
<h1>Done!</h1>
) : promise.pending ? (
<h1>Loading...</h1>
) : (
<h1>Error!</h1>
)}
</host>
);
}
Note: At the type level, autocompletion is conditional on the state you are looking to observe.
Published by UpperCod almost 2 years ago
Exposes at the Any type level, this is an alias for null but at the Atomico level
Published by UpperCod almost 2 years ago
Published by UpperCod almost 2 years ago
Published by UpperCod almost 2 years ago
Atomico through TS declarations extracted all the global constructors available in Window, sometimes these constructors were modified by third-party libraries, which could cause the props to not be inferred correctly.
This version limits the declared types ( If you are a user of any global type not considered, attach it to an issue to add it to the allowed types )
Published by UpperCod about 2 years ago
this new type creates a wrapper to instantiate an element retrieved from the DOM in JSX, example:
import { JSX } from "atomico";
const [ Template ] = useSlot< JSX<{value: number} >>(ref);
<Template value={10}/>
This enhances the technique of referencing slots as templates
Published by UpperCod about 2 years ago
Now lite components correctly receive props.children
Published by UpperCod about 2 years ago
Thanks to @efoken, [email protected] now allows stateless function instances in JSXð, for example:
// Just a plain function, no Custom Element...
const CloseIcon = (props) => (
<svg viewBox="0 0 16 16" width="16" height="16" {...props}>...</svg>
);
// ... can be re-used in JSX like this:
function button() {
return (
<host shadowDom>
<button>
<CloseIcon aria-hidden="true" />
</button>
</host>
);
}
Published by UpperCod about 2 years ago
Published by UpperCod about 2 years ago
This feature allows you to recycle the node and its props, but the hooks will be restarted.
Published by UpperCod about 2 years ago