Started moving over truth-table.tsx from h600878.GitHub.io

This commit is contained in:
Martin Berg Alstad
2023-01-07 16:01:53 +01:00
parent d1ba86a1fe
commit 13be22be57
12 changed files with 735 additions and 9 deletions

113
src/components/input.tsx Normal file
View File

@ -0,0 +1,113 @@
/* @refresh reload */
import { type Component, createSignal, JSX, Setter } from "solid-js";
import type { InputProps } from "../types/interfaces";
import Row from "./row";
function setupEventListener(id: string, setIsHover: Setter<boolean>): () => void {
let isMounted = true;
function hover(hover: boolean): void {
if (isMounted) {
setIsHover(hover);
}
}
const el = document.getElementById(id);
el?.addEventListener("pointerenter", () => hover(true));
el?.addEventListener("pointerleave", () => hover(false));
return () => {
el?.removeEventListener("pointerenter", () => hover(true));
el?.removeEventListener("pointerleave", () => hover(false));
isMounted = false;
}
}
/**
* Sets isText to 'true' or 'false' using the setIsText function.
* if the value of the input element is not empty and it's different from the current value
*/
function setSetIsText(id: string | undefined, isText: boolean, setIsText: Setter<boolean>): void {
if (id) {
const el = document.getElementById(id) as HTMLInputElement | HTMLTextAreaElement | null;
if (el && el.value !== "" !== isText) {
setIsText(el.value !== "");
}
}
}
interface Input<T> extends InputProps<T> {
leading?: JSX.Element,
trailing?: JSX.Element,
}
export const Input: Component<Input<HTMLInputElement>> = (
{
className,
id,
name,
type = "text",
title,
placeholder,
required = false,
onChange,
leading,
trailing
}): JSX.Element => {
/**
* Is 'true' if the input element is in focus
*/
const [isFocused, setIsFocused] = createSignal(false);
/**
* Is 'true' if the user is hovering over the input element
*/
const [isHover, setIsHover] = createSignal(false);
/**
* Is 'true' if the input element contains any characters
*/
const [isText, setIsText] = createSignal(false);
document.addEventListener("DOMContentLoaded", () => {
if (id && title) {
setupEventListener(id, setIsHover);
}
});
return (
<Row className={ "relative" }>
{ leading }
<HoverTitle title={ title } isActive={ isFocused() || isHover() || isText() } htmlFor={ id }/>
<input
class={ `bg-default-bg focus:border-cyan-500 outline-none border-2 border-gray-500
hover:border-t-cyan-400 ${ className }` }
id={ id }
onFocus={ () => setIsFocused(true) }
onBlur={ () => setIsFocused(false) }
name={ name ?? undefined }
type={ type }
placeholder={ placeholder ?? undefined }
required={ required }
onInput={ () => setSetIsText(id, isText(), setIsText) }
onChange={ onChange /*TODO only called after ENTER*/ }/>
{ trailing }
</Row>
);
}
function HoverTitle(
{
title,
isActive = false,
htmlFor
}: { title?: string | null, isActive?: boolean, htmlFor?: string }): JSX.Element {
return (
<label class={ `absolute pointer-events-none
${ isActive ? "-top-2 left-3 default-bg text-sm" : "left-2 top-1" }
transition-all duration-150 text-gray-600 dark:text-gray-400` }
for={ htmlFor }>
<div class={ "z-50 relative" }>{ title }</div>
<div class={ "w-full h-2 default-bg absolute bottom-1/3 z-10" }/>
</label>
);
}

75
src/components/output.tsx Normal file
View File

@ -0,0 +1,75 @@
import { Disclosure, DisclosureButton, DisclosurePanel, Transition } from "solid-headless";
import { Icon } from "solid-heroicons";
import type { ChildProps, TitleProps } from "../types/interfaces";
import { Component, JSX } from "solid-js";
import { chevronUp } from "solid-heroicons/solid";
interface InfoBoxProps extends TitleProps {
error?: boolean,
}
export const InfoBox: Component<InfoBoxProps> = (
{
title = "",
children,
error = false,
className
}: InfoBoxProps): JSX.Element => {
return (
<div class={ `border-rounded ${ error ? "border-red-500" : "border-gray-500" } ${ className }` }>
<p class={ `border-b px-2 ${ error ? "border-red-500" : "border-gray-500" }` }>{ title }</p>
<div class={ "mx-2" }>{ children }</div>
</div>
);
}
interface MyDisclosureProps extends TitleProps {
defaultOpen?: boolean,
onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>,
}
export const MyDisclosure: Component<MyDisclosureProps> = (
{
title,
children,
defaultOpen = false,
className,
id,
onClick
}): JSX.Element => {
return (
<div id={ id } class={ `border-rounded bg-default-bg ${ className }` }>
<Disclosure defaultOpen={ defaultOpen }>
{ ({ isOpen }) =>
<>
<DisclosureButton onClick={ onClick }
class={ `flex-row-center w-full justify-between px-2` }>
<p class={ `py-1` }>{ title }</p>
<Icon path={ chevronUp } class={ `w-5 ${ isOpen() && "transform rotate-180" } transition` } />
</DisclosureButton>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0" show>
<DisclosurePanel>
<div class={ "px-2 pb-2 text-gray-300" }>{ children }</div>
</DisclosurePanel>
</Transition>
</>
}
</Disclosure>
</div>
);
}
export const MyDisclosureContainer: Component<ChildProps> = ({ children, className }): JSX.Element => {
return (
<div class={ `bg-cyan-900 border-rounded dark:border-gray-800 p-2 mb-2
flex flex-col gap-1 ${ className }` }>
{ children }
</div>
);
}

9
src/components/row.tsx Normal file
View File

@ -0,0 +1,9 @@
/* @refresh reload */
import { type Component } from "solid-js";
import type { ChildProps } from "../types/interfaces";
const Row: Component<ChildProps> = ({ children, className }) => {
return <div class={ `flex-row-center ${ className }` }>{ children }</div>
}
export default Row;

View File

@ -0,0 +1,57 @@
/* @refresh reload */
import type { SimpleProps } from "../types/interfaces";
import type { Table } from "../types/interfaces";
import { For } from "solid-js/web";
import { type Component } from "solid-js";
interface TruthTableProps extends SimpleProps {
table?: Table,
header?: string[],
}
const TruthTable: Component<TruthTableProps> = (
{
table,
header,
className,
style,
id,
}) => {
return (
<table class={ `border-2 border-gray-500 border-collapse table z-10 ${ className }` } id={ id }
style={ style }>
<thead>
<tr>
<For each={ header }>
{ (exp) => (
<th scope={ "col" }
class={ `default-bg text-center sticky top-0 [position:-webkit-sticky;]
outline outline-2 outline-offset-[-1px] outline-gray-500` /*TODO sticky header at the top of the screen */ }>
<p class={ "px-2" }>{ exp }</p>
</th>
) }
</For>
</tr>
</thead>
<tbody>
<For each={ table }>
{ (row) =>
<tr class={ "dark:hover:text-black hover:text-white" }>
<For each={ row }>
{ (value) =>
<td class={ `text-center border border-gray-500 last:underline
${ value ? "bg-green-500 dark:bg-green-700" : "bg-red-500 dark:bg-red-700" }` }>
<p>{ value ? "T" : "F" }</p>
</td>
}
</For>
</tr>
}
</For>
</tbody>
</table>
);
}
export default TruthTable;