Skip to content

Commit

Permalink
[refactor] replace CSS Transition with Animate.css
Browse files Browse the repository at this point in the history
[remove] useless Utility methods & types
  • Loading branch information
TechQuery committed Jan 13, 2024
1 parent 18f6cee commit ab5661b
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 123 deletions.
35 changes: 27 additions & 8 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ https://web-cell.dev/scaffold/

- [x] **Path Mode**: `location.hash` (default) & `history.pushState()`

- [x] **Async Loading** (recommend to use with `import()` ECMAScript syntax)
- [x] **Async Loading** (based on `import()` ECMAScript syntax)

- [x] CSS based **Page Transition Animation** (example [CSS][7] & [TSX][8])
- [x] [Animate.css][7] based **Page Transition Animation**

## Installation

Expand Down Expand Up @@ -74,8 +74,14 @@ import { createRouter, PageProps } from 'cell-router';

const { Route, Link } = createRouter();

const TestPage: FC<PageProps> = ({ path, history, defaultSlot, ...data }) => (
<ul>
const TestPage: FC<PageProps> = ({
className,
style,
path,
history,
...data
}) => (
<ul {...{ className, style }}>
<li>Path: {path}</li>
<li>Data: {JSON.stringify(data)}</li>
</ul>
Expand All @@ -88,6 +94,10 @@ new DOMRenderer().render(
<Link to="example/2">Example</Link>
</nav>
<main className="router">
<Route
path=""
component={props => <div {...props}>Home Page</div>}
/>
<Route path="test" component={TestPage} />
<Route path="example/:id" component={TestPage} />
</main>
Expand Down Expand Up @@ -116,8 +126,14 @@ import { createRouter, PageProps } from 'cell-router';

const { Route, Link } = createRouter();

const TestPage: FC<PageProps> = ({ path, history, defaultSlot, ...data }) => (
<ul>
const TestPage: FC<PageProps> = ({
className,
style,
path,
history,
...data
}) => (
<ul {...{ className, style }}>
<li>Path: {path}</li>
<li>Data: {JSON.stringify(data)}</li>
</ul>
Expand All @@ -131,6 +147,10 @@ new DOMRenderer().render(
<Link to="example/2">Example</Link>
</nav>
<main className="router">
<Route
path=""
component={props => <div {...props}>Home Page</div>}
/>
<Route path="test" component={TestPage} />
<Route path="example/:id" component={AsyncPage} />
</main>
Expand All @@ -144,5 +164,4 @@ new DOMRenderer().render(
[4]: https://libraries.io/npm/cell-router
[5]: https://github.com/EasyWebApp/cell-router/actions/workflows/main.yml
[6]: https://nodei.co/npm/cell-router/
[7]: https://github.com/EasyWebApp/cell-router/blob/b7f98243834f9226088c15b111428e211bbfaa9f/test/source/index.less#L5-L25
[8]: https://github.com/EasyWebApp/cell-router/blob/b7f98243834f9226088c15b111428e211bbfaa9f/test/source/page/index.tsx#L19-L32
[7]: https://animate.style/
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cell-router",
"version": "3.0.0-rc.2",
"version": "3.0.0-rc.3",
"license": "LGPL-3.0",
"description": "Web Component Router based on WebCell & MobX",
"keywords": [
Expand Down Expand Up @@ -29,7 +29,7 @@
"mobx": ">=6.11",
"regenerator-runtime": "^0.14.1",
"urlpattern-polyfill": "^9.0.0",
"web-cell": "^3.0.0-rc.6",
"web-cell": "^3.0.0-rc.7",
"web-utility": "^4.1.3"
},
"devDependencies": {
Expand All @@ -47,7 +47,7 @@
"koapache": "^2.2.2",
"lint-staged": "^15.2.0",
"parcel": "~2.11.0",
"prettier": "^3.1.1",
"prettier": "^3.2.1",
"process": "^0.11.10",
"puppeteer-core": "^21.7.0",
"rimraf": "^5.0.5",
Expand Down
16 changes: 8 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 47 additions & 48 deletions source/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import { JsxProps } from 'dom-renderer';
import { computed, observable } from 'mobx';
import {
AnimateCSS,
AnimationType,
ClassComponent,
FunctionComponent,
FC,
WebCell,
WebCellProps,
attribute,
component,
observer,
reaction
observer
} from 'web-cell';

import history, { History } from './History';
import { PageProps, nextTick, watchStop } from './utility';
import { IncludeText, PageProps } from './utility';

export interface CellRouteProps extends JsxProps<HTMLElement> {
export interface CellRouteProps extends WebCellProps {
path: string;
component: FunctionComponent<PageProps> | ClassComponent;
startClass?: string;
endClass?: string;
component: FC<PageProps> | ClassComponent;
inAnimation?: IncludeText<AnimationType, 'In'>;
outAnimation?: IncludeText<AnimationType, 'Out'>;
}

export interface CellRoute extends WebCell {}

@component({ tagName: 'cell-route' })
@observer
export class CellRoute extends HTMLElement {
export class CellRoute extends HTMLElement implements WebCell {
declare props: CellRouteProps;

@attribute
Expand All @@ -32,17 +36,11 @@ export class CellRoute extends HTMLElement {

@attribute
@observable
accessor startClass = '';
accessor inAnimation: CellRouteProps['inAnimation'] = 'fadeIn';

@attribute
@observable
accessor endClass = '';

@observable
accessor moveClass = '';

@observable
accessor moved = !this.endClass;
accessor outAnimation: CellRouteProps['outAnimation'] = 'fadeOut';

@computed
get matched() {
Expand All @@ -54,44 +52,45 @@ export class CellRoute extends HTMLElement {
return History.match(this.path, history.oldPath);
}

@reaction(({ matched }) => matched)
protected async toggleMotion(enter?: any) {
if (!this.startClass || !this.endClass) return;

this.moved = false;
if (enter) {
this.moveClass = this.startClass;
await nextTick();
} else {
const end = watchStop(this, `.${this.endClass}`);
this.moveClass = this.endClass;
await end;
this.moved = true;
}
this.moveClass = undefined;
connectedCallback() {
if (getComputedStyle(this.parentElement).position === 'static')
this.parentElement.style.position = 'relative';
}

render() {
const { matched, oldMatched, component: Page, moveClass, moved } = this,
const { inAnimation, outAnimation, matched, oldMatched } = this,
Tag = this.component,
{ path, oldPath } = history;

return matched ? (
<Page
className={moveClass}
{...matched}
{...History.dataOf(path)}
{...{ path, history }}
<AnimateCSS
type={inAnimation}
component={props => (
<Tag
{...props}
style={{ position: 'absolute', top: '0', left: '0' }}
{...matched}
{...History.dataOf(path)}
{...{ path, history }}
/>
)}
/>
) : oldMatched ? (
<AnimateCSS
type={outAnimation}
component={props => (
<Tag
{...props}
style={{ position: 'absolute', top: '0', left: '0' }}
{...oldMatched}
{...History.dataOf(oldPath)}
path={oldPath}
history={history}
/>
)}
/>
) : (
oldMatched && !moved && (
<Page
className={moveClass}
{...oldMatched}
{...History.dataOf(oldPath)}
path={oldPath}
history={history}
/>
)
<></>
);
}
}
2 changes: 1 addition & 1 deletion source/scope.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PropsWithChildren } from 'web-cell';
import { CellRoute, CellRouteProps } from './Router';

export interface RouterOptions
extends Pick<CellRouteProps, 'startClass' | 'endClass'> {
extends Pick<CellRouteProps, 'inAnimation' | 'outAnimation'> {
mode?: 'hash' | 'history';
}

Expand Down
31 changes: 3 additions & 28 deletions source/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,12 @@ import { JsxProps } from 'dom-renderer';

import { History } from './History';

export function watchStop<T extends HTMLElement | SVGElement>(
root: T,
targetSelector: string
) {
return new Promise<MotionEvent>(resolve => {
function end(event: MotionEvent) {
if (!(event.target as Element).matches(targetSelector)) return;

root.removeEventListener('transitionend', end);
root.removeEventListener('transitioncancel', end);
root.removeEventListener('animationend', end);
root.removeEventListener('animationcancel', end);

resolve(event);
}

root.addEventListener('transitionend', end);
root.addEventListener('transitioncancel', end);
root.addEventListener('animationend', end);
root.addEventListener('animationcancel', end);
});
}
export type IncludeText<Raw extends string, Sub extends string> = {
[K in Raw]: K extends `${string}${Sub}${string}` ? K : never;
}[Raw];

export interface PageProps extends JsxProps<HTMLElement> {
path: string;
history: History;
[key: string]: any;
}

export type MotionEvent = TransitionEvent | AnimationEvent;

export function nextTick() {
return new Promise(resolve => self.requestAnimationFrame(resolve));
}
18 changes: 0 additions & 18 deletions test/source/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,4 @@ nav {
}
.router {
height: 100vh;
overflow: auto;
position: relative;
.page {
position: absolute;
top: 0;
left: 0;
transform: translateX(0);
opacity: 1;
transition: 0.25s;
}
.start {
transform: translateX(100%);
opacity: 0;
}
.end {
transform: translateX(-100%);
opacity: 0;
}
}
6 changes: 3 additions & 3 deletions test/source/page/async.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { FC } from 'web-cell';
import { PageProps } from '../../../source';

const AsyncPage: FC<PageProps> = props => (
<div className={`page ${props.className}`}>
const AsyncPage: FC<PageProps> = ({ path, id, edit, ...props }) => (
<div {...props}>
<h1>Async</h1>
<pre>
<code>{JSON.stringify(props, null, 4)}</code>
<code>{JSON.stringify({ path, id, edit, ...props }, null, 4)}</code>
</pre>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions test/source/page/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export interface TestPageProps extends PageProps {
edit: boolean;
}

export const TestPage: FC<TestPageProps> = ({ className, path, id, edit }) => (
<ul className={`page ${className}`}>
export const TestPage: FC<TestPageProps> = ({ path, id, edit, ...props }) => (
<ul {...props}>
<li>Path: {path}</li>
<li>Data: {JSON.stringify({ id, edit })}</li>
</ul>
Expand Down
Loading

0 comments on commit ab5661b

Please sign in to comment.