Updated dependencies.

Added prettier and formatted.
Fixed bug causing back button to not show at all times.
This commit is contained in:
Martin Berg Alstad
2024-02-25 00:18:02 +01:00
parent ecd9f50029
commit 0528645838
36 changed files with 2815 additions and 1938 deletions

View File

@ -1,57 +1,59 @@
/* @refresh reload */
import { type Component, createSignal } from "solid-js";
import { type Component, createSignal } from "solid-js"
interface SwitchProps extends TitleProps {
defaultValue?: boolean,
onChange?: (value: boolean) => void,
defaultValue?: boolean
onChange?: (value: boolean) => void
}
export const MySwitch: Component<SwitchProps> = (
{
defaultValue = false,
title,
onChange,
className,
name,
id
}) => {
export const MySwitch: Component<SwitchProps> = ({
defaultValue = false,
title,
onChange,
className,
name,
id
}) => {
const [checked, setChecked] = createSignal(defaultValue)
const [checked, setChecked] = createSignal(defaultValue);
function handleChange() {
const newChecked = !checked();
setChecked(newChecked);
if (onChange) {
onChange(newChecked);
}
function handleChange() {
const newChecked = !checked()
setChecked(newChecked)
if (onChange) {
onChange(newChecked)
}
}
return (
<button id={ id }
onClick={ handleChange }
title={ title }
class={ `${ checked() ? "bg-cyan-900" : "bg-gray-500" }
relative inline-flex h-6 w-11 items-center rounded-full my-2 ${ className }` }>
<span class={ "sr-only" }>{ name }</span>
<span class={ `${ checked() ? 'translate-x-6' : 'translate-x-1' }
inline-block h-4 w-4 transform rounded-full bg-white transition-all` } />
</button>
);
};
export const Button: Component<ButtonProps> = (
{
className,
title,
children,
id,
onClick,
type = "button",
}
) => (
<button title={ title } id={ id } type={ type }
class={ `border-rounded bg-cyan-900 px-2 cursor-pointer ${ className }` }
onClick={ onClick }>
{ children }
return (
<button
id={id}
onClick={handleChange}
title={title}
class={`${checked() ? "bg-cyan-900" : "bg-gray-500"} relative my-2 inline-flex h-6 w-11 items-center rounded-full ${className}`}
>
<span class={"sr-only"}>{name}</span>
<span
class={`${checked() ? "translate-x-6" : "translate-x-1"} inline-block h-4 w-4 transform rounded-full bg-white transition-all`}
/>
</button>
);
)
}
export const Button: Component<ButtonProps> = ({
className,
title,
children,
id,
onClick,
type = "button"
}) => (
<button
title={title}
id={id}
type={type}
class={`border-rounded cursor-pointer bg-cyan-900 px-2 ${className}`}
onClick={onClick}
>
{children}
</button>
)

View File

@ -1,23 +1,18 @@
/* @refresh reload */
import { type Component } from "solid-js";
import { Link } from "./link";
import { type Component } from "solid-js"
import { Link } from "./link"
const Card: Component<CardProps> = (
{
children,
className,
title,
to,
newTab = false
}) => (
<div class={ `relative bg-gradient-to-r from-cyan-600 to-cyan-500 h-32 w-72 rounded-2xl ${ className }` }>
<div class={ "relative p-5" }>
<Link className={ "text-white" } to={ to } newTab={ newTab }>
<h3 class={ "text-center w-fit mx-auto before:content-['↗']" }>{ title }</h3>
</Link>
{ children }
</div>
const Card: Component<CardProps> = ({ children, className, title, to, newTab = false }) => (
<div
class={`relative h-32 w-72 rounded-2xl bg-gradient-to-r from-cyan-600 to-cyan-500 ${className}`}
>
<div class={"relative p-5"}>
<Link className={"text-white"} to={to} newTab={newTab}>
<h3 class={"mx-auto w-fit text-center before:content-['↗']"}>{title}</h3>
</Link>
{children}
</div>
);
</div>
)
export default Card;
export default Card

View File

@ -1,104 +1,100 @@
/* @refresh reload */
import { Dialog, DialogDescription, DialogPanel, DialogTitle } from "solid-headless";
import { Component, createEffect, createSignal, JSX } from "solid-js";
import { Button } from "./button";
import { Portal } from "solid-js/web";
import { getElementById } from "../utils/dom";
import { Dialog, DialogDescription, DialogPanel, DialogTitle } from "solid-headless"
import { Component, createEffect, createSignal, JSX } from "solid-js"
import { Button } from "./button"
import { Portal } from "solid-js/web"
import { getElementById } from "../utils/dom"
interface MyDialog extends TitleProps {
description?: string,
button?: JSX.Element,
acceptButtonName?: string,
acceptButtonId?: string,
cancelButtonName?: string,
callback?: () => void,
buttonClass?: string,
buttonTitle?: string | null,
description?: string
button?: JSX.Element
acceptButtonName?: string
acceptButtonId?: string
cancelButtonName?: string
callback?: () => void
buttonClass?: string
buttonTitle?: string | null
}
const MyDialog: Component<MyDialog> = (
{
title,
description,
button,
acceptButtonName = "Ok",
cancelButtonName = "Cancel",
children,
callback,
className,
buttonClass,
buttonTitle,
acceptButtonId,
}) => {
const MyDialog: Component<MyDialog> = ({
title,
description,
button,
acceptButtonName = "Ok",
cancelButtonName = "Cancel",
children,
callback,
className,
buttonClass,
buttonTitle,
acceptButtonId
}) => {
const [isOpen, setIsOpen] = createSignal(false)
const [isOpen, setIsOpen] = createSignal(false);
function callbackAndClose(): void {
callback?.()
setIsOpen(false)
}
function callbackAndClose(): void {
if (callback) {
callback();
}
setIsOpen(false);
function setupKeyPress(): () => void {
let isMounted = true
/**
* Pressing "Enter" when the modal is open, will click the accept button
* @param e KeyboardEvent of keypress
*/
function click(e: KeyboardEvent): void {
if (isMounted && e.key === "Enter" && acceptButtonId) {
getElementById<HTMLButtonElement>(acceptButtonId)?.click()
}
}
function setupKeyPress(): () => void {
let isMounted = true;
if (isOpen()) {
const id = "cl-6"
const el = getElementById(id)
el?.addEventListener("keypress", click)
return () => {
el?.removeEventListener("keypress", click)
isMounted = false
}
} else return () => undefined
}
/**
* Pressing "Enter" when the modal is open, will click the accept button
* @param e KeyboardEvent of keypress
*/
function click(e: KeyboardEvent): void {
if (isMounted && e.key === "Enter" && acceptButtonId) {
getElementById<HTMLButtonElement>(acceptButtonId)?.click();
}
}
createEffect(setupKeyPress, isOpen())
if (isOpen()) {
const id = "cl-6"
const el = getElementById(id);
el?.addEventListener("keypress", click);
return () => {
el?.removeEventListener("keypress", click);
isMounted = false;
}
}
else return () => undefined;
}
return (
<div class={"h-fit w-fit"}>
<button onClick={() => setIsOpen(true)} class={buttonClass} title={buttonTitle ?? undefined}>
{button}
</button>
createEffect(setupKeyPress, isOpen());
<Portal>
<Dialog
isOpen={isOpen()}
onClose={() => setIsOpen(false)}
class={`flex-row-center fixed inset-0 z-50 justify-center overflow-auto text-white ${className}`}
>
<div class={"fixed inset-0 bg-black/40" /*Backdrop*/} aria-hidden={true} />
return (
<div class={ "w-fit h-fit" }>
<DialogPanel class={"border-rounded relative w-fit border-gray-500 bg-default-bg p-2"}>
<DialogTitle class={"border-b"}>{title}</DialogTitle>
<DialogDescription class={"mb-4 mt-1"}>{description}</DialogDescription>
<button onClick={ () => setIsOpen(true) } class={ buttonClass } title={ buttonTitle ?? undefined }>
{ button }
</button>
{children}
<Portal>
<Dialog isOpen={ isOpen() } onClose={ () => setIsOpen(false) }
class={ `fixed inset-0 flex-row-center justify-center z-50 overflow-auto text-white ${ className }` }>
<div class={ "fixed inset-0 bg-black/40" /*Backdrop*/ } aria-hidden={ true } />
<DialogPanel class={ "w-fit relative bg-default-bg border-rounded border-gray-500 p-2" }>
<DialogTitle class={ "border-b" }>{ title }</DialogTitle>
<DialogDescription class={ "mb-4 mt-1" }>{ description }</DialogDescription>
{ children }
<div class={ "my-3" }>
<Button onClick={ callbackAndClose } className={ "h-10 mr-2" }
id={ acceptButtonId }>{ acceptButtonName }</Button>
<Button onClick={ () => setIsOpen(false) }
className={ "h-10" }>{ cancelButtonName }</Button>
</div>
</DialogPanel>
</Dialog>
</Portal>
</div>
);
<div class={"my-3"}>
<Button onClick={callbackAndClose} className={"mr-2 h-10"} id={acceptButtonId}>
{acceptButtonName}
</Button>
<Button onClick={() => setIsOpen(false)} className={"h-10"}>
{cancelButtonName}
</Button>
</div>
</DialogPanel>
</Dialog>
</Portal>
</div>
)
}
export default MyDialog;
export default MyDialog

View File

@ -1,11 +1,13 @@
/* @refresh reload */
import { type Component } from "solid-js";
import { Link } from "./link";
import { type Component } from "solid-js"
import { Link } from "./link"
const Footer: Component<SimpleProps> = ({ className }) => (
<footer class={ `text-center py-5 absolute bottom-0 container ${ className }` }>
<p>Kildekode <Link to={ "https://github.com/h600878/martials.no" }>GitHub</Link></p>
</footer>
);
<footer class={`container absolute bottom-0 py-5 text-center ${className}`}>
<p>
Kildekode <Link to={"https://github.com/h600878/martials.no"}>GitHub</Link>
</p>
</footer>
)
export default Footer;
export default Footer

View File

@ -1,25 +1,29 @@
/* @refresh reload */
import { type Component, Show } from "solid-js";
import { Icon } from "solid-heroicons";
import { chevronLeft } from "solid-heroicons/solid";
import { Link } from "./link";
import { type Component, Show } from "solid-js"
import { Icon } from "solid-heroicons"
import { chevronLeft } from "solid-heroicons/solid"
import { Link } from "./link"
import { useLocation } from "@solidjs/router"
const Header: Component<TitleProps> = ({ className, title = "Title goes here" }) => (
<header class={ className }>
<div class={ "flex-row-center mx-auto w-fit" }>
const Header: Component<TitleProps> = ({ className, title = "Title goes here" }) => {
const location = useLocation()
<Show when={ typeof location !== "undefined" && location.pathname !== "/" } keyed>
<Link to={ "/" } newTab={ false } title={ "Back to homepage" }>
<Icon path={ chevronLeft } class={ "text-cyan-500" } />
</Link>
</Show>
return (
<header class={className}>
<div class={"flex-row-center mx-auto w-fit"}>
<Show when={location.pathname !== "/"} keyed>
<Link to={"/"} newTab={false} title={"Back to homepage"}>
<Icon path={chevronLeft} class={"text-cyan-500"} />
</Link>
</Show>
<h1 class={ "text-center text-cyan-500" }>{ title }</h1>
</div>
<div class={ "mx-auto w-fit" }>
<p>Av Martin Berg Alstad</p>
</div>
<h1 class={"text-center text-cyan-500"}>{title}</h1>
</div>
<div class={"mx-auto w-fit"}>
<p>Av Martin Berg Alstad</p>
</div>
</header>
);
)
}
export default Header;
export default Header

View File

@ -1,28 +1,28 @@
/* @refresh reload */
import { type Component, createSignal, JSX, onMount, Setter, Show } from "solid-js";
import Row from "./row";
import { Icon } from "solid-heroicons";
import { magnifyingGlass, xMark } from "solid-heroicons/solid";
import { getElementById } from "../utils/dom";
import { type Component, createSignal, JSX, onMount, Setter, Show } from "solid-js"
import Row from "./row"
import { Icon } from "solid-heroicons"
import { magnifyingGlass, xMark } from "solid-heroicons/solid"
import { getElementById } from "../utils/dom"
function setupEventListener(id: string, setIsHover: Setter<boolean>): () => void {
let isMounted = true;
let isMounted = true
function hover(hover: boolean): void {
if (isMounted) {
setIsHover(hover);
}
function hover(hover: boolean): void {
if (isMounted) {
setIsHover(hover)
}
}
const el = getElementById(id);
el?.addEventListener("pointerenter", () => hover(true));
el?.addEventListener("pointerleave", () => hover(false));
const el = 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;
}
return () => {
el?.removeEventListener("pointerenter", () => hover(true))
el?.removeEventListener("pointerleave", () => hover(false))
isMounted = false
}
}
/**
@ -30,149 +30,152 @@ function setupEventListener(id: string, setIsHover: Setter<boolean>): () => void
* 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 = getElementById<HTMLInputElement | HTMLTextAreaElement>(id);
if (el && el.value !== "" !== isText) {
setIsText(el.value !== "");
}
if (id) {
const el = getElementById<HTMLInputElement | HTMLTextAreaElement>(id)
if (el && (el.value !== "") !== isText) {
setIsText(el.value !== "")
}
}
}
interface Input<T extends HTMLElement> extends InputProps<T> {
leading?: JSX.Element,
trailing?: JSX.Element,
onChange?: () => void,
inputClass?: string,
leading?: JSX.Element
trailing?: JSX.Element
onChange?: () => void
inputClass?: string
}
export const Input: Component<Input<HTMLInputElement>> = ( // TODO remove leading and trailing from component
{
className,
id,
name,
type = "text",
title,
placeholder,
required = false,
onChange,
leading,
trailing,
inputClass,
ref
}): JSX.Element => {
export const Input: Component<Input<HTMLInputElement>> = (
// TODO remove leading and trailing from component
{
className,
id,
name,
type = "text",
title,
placeholder,
required = false,
onChange,
leading,
trailing,
inputClass,
ref
}
): 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)
/**
* 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);
onMount(() => {
if (id && title) {
setupEventListener(id, setIsHover);
}
});
return (
<Row className={ `relative ${ className }` }>
{ 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 ${ inputClass }` }
id={ id }
ref={ ref }
onFocus={ () => setIsFocused(true) }
onBlur={ () => setIsFocused(false) }
name={ name ?? undefined }
type={ type }
placeholder={ placeholder ?? undefined }
required={ required }
onInput={ () => {
setSetIsText(id, isText(), setIsText);
if (onChange) {
onChange();
}
} } />
{ trailing }
</Row>
);
}
const HoverTitle: Component<{ title?: string, isActive?: boolean, htmlFor?: string }> = (
{
title,
isActive = false,
htmlFor
}) => (
<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>
);
interface SearchProps extends InputProps<HTMLInputElement> {
typingDefault?: boolean
}
export const Search: Component<SearchProps> = (
{
typingDefault = false,
id = "search",
className,
ref
}) => {
const [typing, setTyping] = createSignal(typingDefault);
function getInputElement() {
return getElementById<HTMLInputElement>(id);
onMount(() => {
if (id && title) {
setupEventListener(id, setIsHover)
}
})
function clearSearch(): void {
const el = getInputElement();
if (el) {
el.value = "";
setTyping(false);
history.replaceState(null, "", location.pathname);
el.focus();
}
}
function onChange(): void {
const el = getInputElement();
if (el && (el.value !== "") !== typing()) {
setTyping(el.value !== "");
}
}
return (
<Input inputClass={ `rounded-xl pl-7 h-10 w-full pr-8` } className={ `w-full ${ className }` }
id={ id }
ref={ ref }
placeholder={ "¬A & B -> C" }
type={ "text" }
onChange={ onChange }
leading={ <Icon path={ magnifyingGlass } aria-label={ "Magnifying glass" }
class={ "pl-2 absolute" } /> }
trailing={ <Show when={ typing() } keyed>
<button class={ "absolute right-2" }
title={ "Clear" }
type={ "reset" }
onClick={ clearSearch }>
<Icon path={ xMark } aria-label={ "The letter X" } />
</button>
</Show> }
/>
);
return (
<Row className={`relative ${className}`}>
{leading}
<HoverTitle title={title} isActive={isFocused() || isHover() || isText()} htmlFor={id} />
<input
class={`border-2 border-gray-500 bg-default-bg outline-none hover:border-t-cyan-400
focus:border-cyan-500 ${inputClass}`}
id={id}
ref={ref}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
name={name ?? undefined}
type={type}
placeholder={placeholder ?? undefined}
required={required}
onInput={() => {
setSetIsText(id, isText(), setIsText)
if (onChange) {
onChange()
}
}}
/>
{trailing}
</Row>
)
}
const HoverTitle: Component<{ title?: string; isActive?: boolean; htmlFor?: string }> = ({
title,
isActive = false,
htmlFor
}) => (
<label
class={`pointer-events-none absolute
${isActive ? "default-bg -top-2 left-3 text-sm" : "left-2 top-1"}
text-gray-600 transition-all duration-150 dark:text-gray-400`}
for={htmlFor}
>
<div class={"relative z-50"}>{title}</div>
<div class={"default-bg absolute bottom-1/3 z-10 h-2 w-full"} />
</label>
)
interface SearchProps extends InputProps {
typingDefault?: boolean
}
export const Search: Component<SearchProps> = ({
typingDefault = false,
id = "search",
className,
ref
}) => {
const [typing, setTyping] = createSignal(typingDefault)
function getInputElement() {
return getElementById<HTMLInputElement>(id)
}
function clearSearch(): void {
const el = getInputElement()
if (el) {
el.value = ""
setTyping(false)
history.replaceState(null, "", location.pathname)
el.focus()
}
}
function onChange(): void {
const el = getInputElement()
if (el && (el.value !== "") !== typing()) {
setTyping(el.value !== "")
}
}
return (
<Input
inputClass={`rounded-xl pl-7 h-10 w-full pr-8`}
className={`w-full ${className}`}
id={id}
ref={ref}
placeholder={"¬A & B -> C"}
type={"text"}
onChange={onChange}
leading={
<Icon path={magnifyingGlass} aria-label={"Magnifying glass"} class={"absolute pl-2"} />
}
trailing={
<Show when={typing()} keyed>
<button class={"absolute right-2"} title={"Clear"} type={"reset"} onClick={clearSearch}>
<Icon path={xMark} aria-label={"The letter X"} />
</button>
</Show>
}
/>
)
}

View File

@ -1,23 +1,18 @@
/* @refresh reload */
import { type Component } from "solid-js";
import Header from "./header";
import Footer from "./footer";
import { type Component } from "solid-js"
import Header from "./header"
import Footer from "./footer"
const Layout: Component<TitleProps> = (
{
children,
title,
className
}) => (
<div class={ `bg-default-bg text-white min-h-screen relative font-mono ${ className }` }>
<div class="container mx-auto">
<Header className={ "py-3" } title={ title } />
<main>
<div class={ "pb-28" }>{ children }</div>
<Footer />
</main>
</div>
const Layout: Component<TitleProps> = ({ children, title, className }) => (
<div class={`relative min-h-screen bg-default-bg font-mono text-white ${className}`}>
<div class="container mx-auto">
<Header className={"py-3"} title={title} />
<main>
<div class={"pb-28"}>{children}</div>
<Footer />
</main>
</div>
);
</div>
)
export default Layout;
export default Layout

View File

@ -1,20 +1,17 @@
/* @refresh reload */
import { type Component } from "solid-js";
import { type Component } from "solid-js"
export const Link: Component<LinkProps> = (
{
to,
rel,
children,
className,
id,
newTab = true,
title,
}) => ( // TODO <A/> throws exception
<a href={ to } id={ id } title={ title }
rel={ `${ rel } ${ newTab ? "noreferrer" : undefined }` }
target={ newTab ? "_blank" : undefined }
class={ `link ${ className }` }>
{ children }
</a>
);
{ to, rel, children, className, id, newTab = true, title } // TODO <A/> throws exception
) => (
<a
href={to}
id={id}
title={title}
rel={`${rel} ${newTab ? "noreferrer" : undefined}`}
target={newTab ? "_blank" : undefined}
class={`link ${className}`}
>
{children}
</a>
)

View File

@ -1,78 +1,72 @@
/* @refresh reload */
import { type Component, createEffect, createSignal, JSX, Show } from "solid-js";
import { Button } from "./button";
import { type Component, createEffect, createSignal, JSX, Show } from "solid-js"
import { Button } from "./button"
interface MenuProps extends TitleProps {
button?: JSX.Element,
buttonClassName?: string,
itemsClassName?: string,
button?: JSX.Element
buttonClassName?: string
itemsClassName?: string
}
const MyMenu: Component<MenuProps> = (
{
title,
button,
children,
id,
className,
buttonClassName,
itemsClassName,
}) => {
const MyMenu: Component<MenuProps> = ({
title,
button,
children,
id,
className,
buttonClassName,
itemsClassName
}) => {
const [isOpen, setIsOpen] = createSignal(false)
const [isOpen, setIsOpen] = createSignal(false);
function closeMenu(): void {
setIsOpen(false)
}
function closeMenu(): void {
setIsOpen(false);
function toggleMenu(): void {
setIsOpen(!isOpen())
}
createEffect(() => {
function click(e: MouseEvent): void {
if (e.target instanceof HTMLElement) {
if (e.target.closest(`#${id}`) === null) {
closeMenu()
}
}
}
function toggleMenu(): void {
setIsOpen(!isOpen());
function keypress(e: KeyboardEvent): void {
if (e.key === "Escape") {
closeMenu()
}
}
createEffect(() => {
if (isOpen()) {
document.addEventListener("click", click)
document.addEventListener("keyup", keypress)
} else {
document.removeEventListener("click", click)
document.removeEventListener("keyup", keypress)
}
})
function click(e: MouseEvent): void {
if (e.target instanceof HTMLElement) {
if (e.target.closest(`#${ id }`) === null) {
closeMenu();
}
}
}
function keypress(e: KeyboardEvent): void {
if (e.key === "Escape") {
closeMenu();
}
}
if (isOpen()) {
document.addEventListener("click", click);
document.addEventListener("keyup", keypress);
}
else {
document.removeEventListener("click", click);
document.removeEventListener("keyup", keypress);
}
});
return ( // TODO transition
<div class={ `${ className }` } id={ id }>
<Button title={ title }
onClick={ toggleMenu }
className={ `flex-row-center ${ buttonClassName }` }>
{ button }
</Button>
<Show when={ isOpen() } keyed>
<div
class={ `absolute bg-default-bg border border-gray-500 rounded-b-xl mt-1 w-max z-50 ${ itemsClassName }` }>
<div class={ "mx-1" }>{ children }</div>
</div>
</Show>
return (
// TODO transition
<div class={`${className}`} id={id}>
<Button title={title} onClick={toggleMenu} className={`flex-row-center ${buttonClassName}`}>
{button}
</Button>
<Show when={isOpen()} keyed>
<div
class={`absolute z-50 mt-1 w-max rounded-b-xl border border-gray-500 bg-default-bg ${itemsClassName}`}
>
<div class={"mx-1"}>{children}</div>
</div>
);
</Show>
</div>
)
}
export default MyMenu;
export default MyMenu

View File

@ -1,74 +1,65 @@
/* @refresh reload */
import { Disclosure, DisclosureButton, DisclosurePanel, Transition } from "solid-headless";
import { Icon } from "solid-heroicons";
import { type Component, JSX } from "solid-js";
import { chevronUp } from "solid-heroicons/solid";
import { Disclosure, DisclosureButton, DisclosurePanel, Transition } from "solid-headless"
import { Icon } from "solid-heroicons"
import { type Component, JSX } from "solid-js"
import { chevronUp } from "solid-heroicons/solid"
interface InfoBoxProps extends TitleProps {
error?: boolean,
error?: boolean
}
export const InfoBox: Component<InfoBoxProps> = (
{
title,
children,
error = false,
className
}) => (
<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>
);
export const InfoBox: Component<InfoBoxProps> = ({ title, children, error = false, className }) => (
<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>,
defaultOpen?: boolean
onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>
}
export const MyDisclosure: Component<MyDisclosureProps> = (
{
title,
children,
defaultOpen = false,
className,
id,
onClick
}): JSX.Element => (
<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 MyDisclosure: Component<MyDisclosureProps> = ({
title,
children,
defaultOpen = false,
className,
id,
onClick
}): JSX.Element => (
<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() && "rotate-180 transform"} 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
}) => (
<div class={ `bg-cyan-900 border-rounded dark:border-gray-800 p-2 mb-2
flex flex-col gap-1 ${ className }` }>
{ children }
</div>
);
export const MyDisclosureContainer: Component<ChildProps> = ({ children, className }) => (
<div
class={`border-rounded mb-2 flex flex-col gap-1
bg-cyan-900 p-2 dark:border-gray-800 ${className}`}
>
{children}
</div>
)

View File

@ -1,5 +1,5 @@
/* @refresh reload */
import { type Component } from "solid-js";
import { type Component } from "solid-js"
/**
* A row that centers its children
@ -8,7 +8,7 @@ import { type Component } from "solid-js";
* @returns The row
*/
const Row: Component<ChildProps> = ({ children, className }) => (
<div class={ `flex-row-center ${ className }` }>{ children }</div>
);
<div class={`flex-row-center ${className}`}>{children}</div>
)
export default Row;
export default Row

View File

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