Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SolidJS #39

Merged
merged 3 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading