Skip to content

Commit

Permalink
Add SolidJS (#39)
Browse files Browse the repository at this point in the history
* WIP: Add solid-js

* Fix solid-js timer

* Add solidjs flight booker
  • Loading branch information
andrewiggins committed Oct 16, 2023
1 parent be718b3 commit df57dd0
Show file tree
Hide file tree
Showing 15 changed files with 574 additions and 32 deletions.
12 changes: 12 additions & 0 deletions frameworks/solid-js/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"loose": true,
"modules": false
}
],
["solid"]
]
}
9 changes: 9 additions & 0 deletions frameworks/solid-js/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../jsconfig.json",
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "solid-js"
},
"exclude": ["node_modules", "dist"],
"include": ["./**/*"]
}
20 changes: 20 additions & 0 deletions frameworks/solid-js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "solid-js-apps",
"private": true,
"version": "0.0.1",
"license": "MIT",
"scripts": {
"build": "rollup -c rollup.config.js",
"watch": "rollup -c rollup.config.js --watch",
"tsc": "tsc -p ./jsconfig.json --noEmit"
},
"dependencies": {
"solid-js": "^1.8.1"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@rollup/plugin-babel": "6.0.0",
"babel-preset-solid": "^1.8.0"
}
}
10 changes: 10 additions & 0 deletions frameworks/solid-js/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const babel = require("@rollup/plugin-babel").default;
const { generateConfigs } = require("../bundleHelpers");

const plugins = () => [
babel({
babelHelpers: "bundled"
})
];

module.exports = generateConfigs("solid-js", plugins);
18 changes: 18 additions & 0 deletions frameworks/solid-js/src/7GUIs-counter/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createSignal } from "solid-js";
import { render } from "solid-js/web";

function App() {
const [count, setCount] = createSignal(0);
return (
<button
class="btn badge"
data-badge={count()}
style={{ "margin-top": "0.5rem" }}
onClick={() => setCount(count() + 1)}
>
count: {count()}
</button>
);
}

render(App, document.getElementById("app"));
31 changes: 31 additions & 0 deletions frameworks/solid-js/src/7GUIs-flight-booker/DateEntry.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @typedef DateEntryProps
* @property {string} label
* @property {string} date
* @property {(newDate: string) => void} setDate
* @property {string | null} errorMsg
* @property {boolean} [disabled]
*/

/**
* @param {DateEntryProps} props
*/
export function DateEntry(props) {
const inputId = `${props.label}-date`;
return (
<div class={"form-group" + (props.errorMsg ? " has-error" : "")}>
<label class="form-label" for={inputId}>
{props.label}
</label>
<input
id={inputId}
class="form-input"
type="text"
value={props.date}
onInput={e => props.setDate(e.currentTarget.value)}
disabled={props.disabled}
/>
{props.errorMsg && <p class="form-input-hint">{props.errorMsg}</p>}
</div>
);
}
24 changes: 24 additions & 0 deletions frameworks/solid-js/src/7GUIs-flight-booker/TripType.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export const ONE_WAY_FLIGHT = "one-way";
export const RETURN_FLIGHT = "return";

/**
* @param {{ tripType: string; setTripType(value: string): void; }} props
*/
export function TripType(props) {
return (
<div class="form-group">
<label class="form-label" for="trip-type">
Trip type
</label>
<select
id="trip-type"
class="form-select"
value={props.tripType}
onInput={e => props.setTripType(e.currentTarget.value)}
>
<option value={ONE_WAY_FLIGHT}>one-way flight</option>
<option value={RETURN_FLIGHT}>return flight</option>
</select>
</div>
);
}
98 changes: 98 additions & 0 deletions frameworks/solid-js/src/7GUIs-flight-booker/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { createMemo, createSignal } from "solid-js";
import { render } from "solid-js/web";
import { today, validateDate } from "../../../../lib/date";
import { TripType, RETURN_FLIGHT, ONE_WAY_FLIGHT } from "./TripType";
import { DateEntry } from "./DateEntry";

const initialDate = today();

/**
* @typedef {import('solid-js').Accessor<T>} Accessor
* @template T
*/

/**
* @param {string} initialDate
* @returns {[Accessor<string>, Accessor<string | null>, (string) => void]}
*/
function useDate(initialDate) {
const [date, setDate] = createSignal(initialDate);
const [error, setError] = createSignal(null);

/** @type {(newDate: string) => void} */
function updateDate(newDate) {
setDate(newDate);

try {
validateDate(newDate);
setError(null);
} catch (error) {
setError(error.message);
}
}

return [date, error, updateDate];
}

function App() {
const [tripType, setTripType] = createSignal(ONE_WAY_FLIGHT);
const [departing, departingError, setDeparting] = useDate(initialDate);
const [returning, returningError, setReturning] = useDate(initialDate);

const finalReturningError = createMemo(() => {
if (
departingError() == null &&
returningError() == null &&
tripType() == RETURN_FLIGHT &&
returning() < departing()
) {
return "Returning date must be on or after departing date.";
} else {
return returningError();
}
});

function bookFlight() {
const type = tripType() === RETURN_FLIGHT ? "return" : "one-way";

let message = `You have booked a ${type} flight, departing ${departing()}`;
if (tripType() == RETURN_FLIGHT) {
message += ` and returning ${returning()}`;
}

alert(message);
}

return (
<>
<TripType
tripType={tripType()}
setTripType={value => setTripType(value)}
/>
<DateEntry
label="Departing"
date={departing()}
setDate={newDate => setDeparting(newDate)}
errorMsg={departingError()}
/>
<DateEntry
label="Returning"
date={returning()}
setDate={newDate => setReturning(newDate)}
errorMsg={finalReturningError()}
disabled={tripType() !== RETURN_FLIGHT}
/>
<div class="form-group">
<button
disabled={Boolean(departingError() || finalReturningError())}
onClick={bookFlight}
class="btn btn-primary"
>
book
</button>
</div>
</>
);
}

render(App, document.getElementById("app"));
36 changes: 36 additions & 0 deletions frameworks/solid-js/src/7GUIs-temp-converter/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createSignal } from "solid-js";
import { render } from "solid-js/web";

function App() {
const [f, setF] = createSignal(null);
const [c, setC] = createSignal(null);

function setBothFromC(value) {
setC(value);
setF(+(32 + (9 / 5) * value).toFixed(1));
}

function setBothFromF(value) {
setF(value);
setC(+((5 / 9) * (value - 32)).toFixed(1));
}

return (
<>
<input
value={c()}
onInput={e => setBothFromC(e.currentTarget.value)}
type="number"
/>{" "}
°c ={" "}
<input
value={f()}
onInput={e => setBothFromF(e.currentTarget.value)}
type="number"
/>{" "}
°f
</>
);
}

render(App, document.getElementById("app"));
72 changes: 72 additions & 0 deletions frameworks/solid-js/src/7GUIs-timer/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
createSignal,
onCleanup,
createMemo,
createRenderEffect
} from "solid-js";
import { render } from "solid-js/web";

function App() {
const [lastRenderTime, setLastRenderTime] = createSignal(performance.now());
const [elapsed, setElapsed] = createSignal(0);
const [duration, setDuration] = createSignal(5000);
const formattedElapsed = createMemo(() => (elapsed() / 1000).toFixed(1));

/** @type {number | null} */
let frame = null;
function requestFrame() {
// Always access signals first so Solid can track the dependencies,
// regardless of if the frame is null or not.
if (elapsed() < duration() && frame == null) {
frame = requestAnimationFrame(now => {
frame = null;
const timeToAdd = Math.min(
now - lastRenderTime(),
duration() - elapsed()
);
setElapsed(prevElapsed => prevElapsed + timeToAdd);
setLastRenderTime(now);
});
}
}

createRenderEffect(() => requestFrame());

onCleanup(() => {
if (frame != null) {
cancelAnimationFrame(frame);
}
});

return (
<>
<label>
Elapsed time: <progress value={elapsed() / duration()} />
</label>
<div class="elapsed">{formattedElapsed()}s</div>
<label>
Duration:{" "}
<input
type="range"
min="1"
max="20000"
value={duration()}
onInput={e => setDuration(e.currentTarget.valueAsNumber)}
/>
</label>
<div>
<button
class="btn btn-primary"
onClick={() => {
setElapsed(0);
setLastRenderTime(performance.now());
}}
>
Reset
</button>
</div>
</>
);
}

render(App, document.getElementById("app"));
4 changes: 4 additions & 0 deletions frameworks/solid-js/src/hello-world/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { render } from "solid-js/web";

const HelloWorld = () => <div>Hello World!</div>;
render(HelloWorld, document.getElementById("app"));
Loading

0 comments on commit df57dd0

Please sign in to comment.