Skip to content

Commit

Permalink
Feat/counter example (#10)
Browse files Browse the repository at this point in the history
* FEAT/counter example added

* FEAT/typescript supports added
  • Loading branch information
AugustinSorel authored Dec 31, 2023
1 parent 3371499 commit 395501f
Show file tree
Hide file tree
Showing 12 changed files with 408 additions and 3,734 deletions.
30 changes: 30 additions & 0 deletions example/counter/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "counter",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack serve",
"build": "webpack --mode=production"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/preset-env": "^7.23.7",
"@babel/preset-typescript": "^7.23.3",
"@babel/register": "^7.23.7",
"@types/node": "^20.10.6",
"css-loader": "^6.8.1",
"html-webpack-plugin": "^5.6.0",
"rollup-plugin-dts": "^6.1.0",
"style-loader": "^3.3.3",
"ts-loader": "^9.5.1",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
"dependencies": {
"SerendipJS": "workspace:^"
}
}
Empty file added example/counter/src/app.ts
Empty file.
94 changes: 94 additions & 0 deletions example/counter/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");

:root {
--background-primary: #191114;
--background-secondary: #381525;
--background-secondary-hover: #5c1839;

--ring-primary: #873356;
--ring-secondary: #6d2545;

--foreground-primary: #ff92ad;
}

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
color: var(--foreground-primary);
background-color: var(--background-primary);
font-family: "Roboto", sans-serif;
}

.main-container {
display: flex;
justify-content: center;
align-items: center;
gap: 5rem;
min-height: 100vh;
max-width: 75%;
margin-inline: auto;
}

.text-massive {
font-size: 7rem;
}

.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.btn {
position: relative;
border: none;
height: 7rem;
aspect-ratio: 1/1;
display: flex;
justify-content: center;
align-items: center;
color: currentColor;
background-color: var(--background-secondary);
transition: background-color 100ms ease;
cursor: pointer;
border-radius: 100%;
}

.btn:focus-visible,
.btn:hover {
background-color: var(--background-secondary-hover);
outline: 5px solid var(--ring-primary);
}

.tooltip:focus-visible::after,
.tooltip:hover::after {
opacity: 1;
translate: -50% 0;
transition-delay: 500ms;
pointer-events: auto;
}

.tooltip::after {
content: attr(data-tooltip);
font-size: initial;
font-weight: bold;
padding: 1rem 2rem;
border: 3px solid var(--ring-secondary);
position: absolute;
top: calc(100% + 1rem);
left: 50%;
translate: -50% 0.5rem;
background-color: var(--background-secondary);
border-radius: 0.5rem;
opacity: 0;
pointer-events: none;

transition-delay: 0;
transition-property: opactiy translate;
transition-duration: 100ms;
transition-timing-function: ease;
}
11 changes: 11 additions & 0 deletions example/counter/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>todo list example</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
39 changes: 39 additions & 0 deletions example/counter/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createApp, h, hString, Reducer, View } from "SerendipJS";
import "./index.css";

const state = { count: 0 };

const View: View<typeof state> = (state, emit) => {
return h("main", { class: "main-container" }, [
h(
"button",
{
class: "btn text-massive tooltip",
"data-tooltip": "decrement",
on: { click: () => emit("sub") },
},
[hString("-")],
),
h("span", { class: "text-massive truncate" }, [
hString(state.count.toString()),
]),
h(
"button",
{
class: "btn text-massive tooltip",
"data-tooltip": "increment",
on: { click: () => emit("add", 100) },
},
[hString("+")],
),
]);
};

const reducers: Reducer<typeof state> = {
add: (state) => ({ count: state.count }),
sub: (state) => ({ count: state.count - 1 }),
};

createApp({ state, view: View, reducers }).mount(
document.getElementById("app")!,
);
7 changes: 7 additions & 0 deletions example/counter/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"noImplicitAny": false
},
"extends": "../../tsconfig.json",
"include": ["src/**/*", "rollup.config.ts"]
}
55 changes: 55 additions & 0 deletions example/counter/webpack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import HtmlWebpackPlugin from "html-webpack-plugin";
import { Configuration } from "webpack";
import "webpack-dev-server";
import path from "path";

const config: Configuration = {
mode: "development",
entry: {
bundle: path.resolve(__dirname, "./src/index.ts"),
},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "[name].[contenthash].js",
clean: true,
},
devServer: {
static: {
directory: path.resolve(__dirname, "./dist"),
},
port: 3001,
hot: true,
compress: true,
historyApiFallback: true,
},
module: {
rules: [
{
test: /\.ts?$/,
exclude: /node_modules/,
use: "ts-loader",
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: "ts-loader",
},
{
test: /\.css?$/,
exclude: /node_modules/,
use: ["style-loader", "css-loader"],
},
],
},
resolve: {
extensions: [".ts", ".js", ".tsx"],
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/index.html",
}),
],
};

export default config;
31 changes: 24 additions & 7 deletions packages/runtime/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,36 @@ import { Dispatcher } from "./dispatcher";
import { VNodes } from "./h";
import { mountDOM } from "./mount-dom";

type Props = {
state: any;
view: (state: any, emit: any) => VNodes;
reducers: Record<string, Function>;
export type Reducer<TState> = Record<
string,
<TPayload = unknown>(state: TState, payload?: TPayload) => TState
>;

export type View<TState> = (
state: TState,
emit: <TPayload = unknown>(name: string, payload?: TPayload) => void,
) => VNodes;

type Props<TState, TReducers extends Reducer<TState>> = {
state: TState;
view: View<TState>;
reducers: TReducers;
};

export function createApp({ state, view, reducers }: Props) {
export const createApp = <TState, TReducers extends Reducer<TState>>({
state,
view,
reducers,
}: Props<TState, TReducers>) => {
let parentEl: HTMLElement | null = null;
let vdom: VNodes | null = null;

const dispatcher = new Dispatcher();

const emit = (eventName: string, payload: any) => {
const emit = <TPayload extends unknown>(
eventName: string,
payload?: TPayload,
) => {
dispatcher.dispatch(eventName, payload);
};

Expand Down Expand Up @@ -59,4 +76,4 @@ export function createApp({ state, view, reducers }: Props) {
}
},
};
}
};
59 changes: 59 additions & 0 deletions packages/runtime/src/attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export function setAttributes(el: HTMLElement, attrs: any) {
const { class: className, style, ...otherAttrs } = attrs;

delete otherAttrs.key;

if (className) {
setClass(el, className);
}

if (style) {
Object.entries(style).forEach(([prop, value]) => {
setStyle(el, prop, value);
});
}

for (const [name, value] of Object.entries(otherAttrs)) {
setAttribute(el, name, value);
}
}

export function setAttribute(el: HTMLElement, name: string, value: any) {
if (!value) {
removeAttribute(el, name);
} else {
el.setAttribute(name, value);
}
}

export function removeAttribute(el: HTMLElement, name: string) {
try {
//@ts-expect-error
el[name] = null;
} catch {
console.warn(`Failed to set "${name}" to null on ${el.tagName}`);
}

el.removeAttribute(name);
}

export function setStyle(el: HTMLElement, name: any, value: any) {
el.style[name] = value;
}

export function removeStyle(el: HTMLElement, name: string) {
//@ts-expect-error
el.style[name] = null;
}

function setClass(el: HTMLElement, className: string | string[]) {
el.className = "";

if (typeof className === "string") {
el.className = className;
}

if (Array.isArray(className)) {
el.classList.add(...className);
}
}
1 change: 0 additions & 1 deletion packages/runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./h";
export * from "./mount-dom";
export * from "./app";
Loading

0 comments on commit 395501f

Please sign in to comment.