We use the same configuration as Parcel to bundle this sandbox, you
can find more info about Parcel
diff --git a/source/Animation/index.tsx b/source/Animation/index.tsx
new file mode 100644
index 0000000..80546b4
--- /dev/null
+++ b/source/Animation/index.tsx
@@ -0,0 +1,60 @@
+import { observable } from 'mobx';
+import { importCSS } from 'web-utility';
+
+import { WebCellProps } from '../Async';
+import { animated } from '../MobX';
+import { WebCell, component } from '../WebCell';
+import { FC, attribute, observer, reaction } from '../decorator';
+import { AnimationType } from './type';
+
+export * from './type';
+
+export interface AnimateCSS extends WebCell {}
+
+export interface AnimateCSSProps {
+ type: AnimationType;
+ component: FC
;
+}
+
+@component({ tagName: 'animation-css' })
+@observer
+export class AnimateCSS extends HTMLElement implements WebCell {
+ declare props: AnimateCSSProps;
+
+ @attribute
+ @observable
+ accessor type: AnimationType;
+
+ @attribute
+ @observable
+ accessor playing = false;
+
+ component: FC;
+
+ async connectedCallback() {
+ await importCSS('https://unpkg.com/animate.css@4/animate.min.css');
+
+ this.typeChanged();
+ }
+
+ @reaction(({ type }) => type)
+ async typeChanged() {
+ this.playing = true;
+
+ await animated(this, '.animate__animated');
+
+ this.playing = false;
+ }
+
+ render() {
+ const { type, playing, component: Tag } = this;
+
+ return playing ? (
+
+ ) : type.includes('Out') ? (
+ <>>
+ ) : (
+
+ );
+ }
+}
diff --git a/source/Animation/type.ts b/source/Animation/type.ts
new file mode 100644
index 0000000..9fd98d2
--- /dev/null
+++ b/source/Animation/type.ts
@@ -0,0 +1,56 @@
+export type PositionY = 'Top' | 'Bottom';
+export type DirectionX = 'Left' | 'Right';
+export type DirectionY = 'Up' | 'Down';
+export type Direction = DirectionX | DirectionY;
+export type AnimationMode = 'In' | 'Out';
+
+export type AttentionSeekers =
+ | 'bounce'
+ | 'flash'
+ | 'pulse'
+ | 'rubberBand'
+ | `shake${'X' | 'Y'}`
+ | 'headShake'
+ | 'swing'
+ | 'tada'
+ | 'wobble'
+ | 'jello'
+ | 'heartBeat';
+export type BackEntrances = `backIn${Direction}`;
+export type BackExits = `backOut${Direction}`;
+export type BouncingEntrances = `bounceIn${'' | Direction}`;
+export type BouncingExits = `bounceOut${'' | Direction}`;
+export type FadingEntrances =
+ | `fadeIn${'' | `${Direction}${'' | 'Big'}`}`
+ | `fadeIn${PositionY}${DirectionX}`;
+export type FadingExits = `fadeOut${
+ | ''
+ | `${Direction}${'' | 'Big'}`
+ | `${PositionY}${DirectionX}`}`;
+export type Flippers = `flip${'' | `${AnimationMode}${'X' | 'Y'}`}`;
+export type Lightspeed = `lightSpeed${AnimationMode}${DirectionX}`;
+export type RotatingEntrances = `rotateIn${'' | `${DirectionY}${DirectionX}`}`;
+export type RotatingExits = `rotateOut${'' | `${DirectionY}${DirectionX}`}`;
+export type Specials = 'hinge' | 'jackInTheBox' | `roll${'In' | 'Out'}`;
+export type ZoomingEntrances = `zoomIn${'' | Direction}`;
+export type ZoomingExits = `zoomOut${'' | Direction}`;
+export type SlidingEntrances = `slideIn${Direction}`;
+export type SlidingExits = `slideOut${Direction}`;
+
+export type AnimationType =
+ | AttentionSeekers
+ | BackEntrances
+ | BackExits
+ | BouncingEntrances
+ | BouncingExits
+ | FadingEntrances
+ | FadingExits
+ | Flippers
+ | Lightspeed
+ | RotatingEntrances
+ | RotatingExits
+ | Specials
+ | ZoomingEntrances
+ | ZoomingExits
+ | SlidingEntrances
+ | SlidingExits;
diff --git a/source/Async.tsx b/source/Async.tsx
index 04334d2..c80bb8d 100644
--- a/source/Async.tsx
+++ b/source/Async.tsx
@@ -1,5 +1,5 @@
-import { observable } from 'mobx';
import { JsxProps } from 'dom-renderer';
+import { observable } from 'mobx';
import { ClassComponent, WebCell, component } from './WebCell';
import {
@@ -7,42 +7,39 @@ import {
FunctionComponent,
PropsWithChildren,
WebCellComponent,
- observer,
- reaction
+ observer
} from './decorator';
export type ComponentTag = string | WebCellComponent;
export type WebCellProps = JsxProps;
-export interface AsyncBoxProps extends WebCellProps {
+export interface AsyncCellProps extends WebCellProps {
loader: () => Promise;
delegatedProps?: WebCellProps;
}
-export interface AsyncBox extends WebCell {}
+export interface AsyncCell extends WebCell {}
@component({
- tagName: 'async-box'
+ tagName: 'async-cell'
})
@observer
-export class AsyncBox extends HTMLElement {
- declare props: AsyncBoxProps;
+export class AsyncCell extends HTMLElement {
+ declare props: AsyncCellProps;
- @observable
- accessor loader: AsyncBoxProps['loader'];
+ loader: AsyncCellProps['loader'];
@observable
accessor component: FC;
@observable
- accessor delegatedProps: AsyncBoxProps['delegatedProps'];
+ accessor delegatedProps: AsyncCellProps['delegatedProps'];
connectedCallback() {
this.load();
}
- @reaction((element: AsyncBox) => element.loader)
protected async load() {
this.component = undefined;
@@ -72,7 +69,7 @@ export function lazy<
T extends () => Promise<{ default: FunctionComponent | ClassComponent }>
>(loader: T) {
return (props: GetAsyncProps) => (
- (await loader()).default}
/>
diff --git a/source/MobX.ts b/source/MobX.ts
index da8fe2e..7dcec68 100644
--- a/source/MobX.ts
+++ b/source/MobX.ts
@@ -1,5 +1,6 @@
import { DataObject } from 'dom-renderer';
import { ObservableValue } from 'mobx/dist/internal';
+import { delegate } from 'web-utility';
export function getMobxData(observable: T) {
for (const key of Object.getOwnPropertySymbols(observable)) {
@@ -13,3 +14,18 @@ export function getMobxData(observable: T) {
) as T;
}
}
+
+export const animated = (
+ root: T,
+ targetSelector: string
+) =>
+ new Promise(resolve => {
+ const ended = delegate(targetSelector, (event: AnimationEvent) => {
+ root.removeEventListener('animationend', ended);
+ root.removeEventListener('animationcancel', ended);
+ resolve(event);
+ });
+
+ root.addEventListener('animationend', ended);
+ root.addEventListener('animationcancel', ended);
+ });
diff --git a/source/decorator.ts b/source/decorator.ts
index dd2bd3a..f901aa0 100644
--- a/source/decorator.ts
+++ b/source/decorator.ts
@@ -52,6 +52,8 @@ function wrapClass(Component: T) {
extends (Component as ClassComponent)
implements CustomElement
{
+ static observedAttributes = [];
+
protected disposers: IReactionDisposer[] = [];
get props() {
@@ -90,6 +92,16 @@ function wrapClass(Component: T) {
this.disposers.length = 0;
}
+ setAttribute(name: string, value: string) {
+ const old = super.getAttribute(name),
+ names: string[] = this.constructor['observedAttributes'];
+
+ super.setAttribute(name, value);
+
+ if (names.includes(name))
+ this.attributeChangedCallback(name, old, value);
+ }
+
attributeChangedCallback(name: string, old: string, value: string) {
this[toCamelCase(name)] = parseJSON(value);
@@ -141,18 +153,10 @@ export function attribute(
{ name, addInitializer }: ClassAccessorDecoratorContext
) {
addInitializer(function () {
- const { constructor } = this;
- var names = constructor['observedAttributes'];
+ const names: string[] = this.constructor['observedAttributes'],
+ attribute = toHyphenCase(name.toString());
- if (!names) {
- names = [];
-
- Object.defineProperty(constructor, 'observedAttributes', {
- configurable: true,
- get: () => names
- });
- }
- names.push(toHyphenCase(name.toString()));
+ if (!names.includes(attribute)) names.push(attribute);
});
}
diff --git a/source/index.ts b/source/index.ts
index a68df65..b23dd9f 100644
--- a/source/index.ts
+++ b/source/index.ts
@@ -3,3 +3,4 @@ export * from './MobX';
export * from './WebCell';
export * from './WebField';
export * from './Async';
+export * from './Animation';