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

Ergonomic SSR #78

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
},
"editor.codeActionsOnSave": {
"quickfix.biome": "always",
"source.fixAll.biome": "always",
"source.organizeImports.biome": "always"
},
"css.customData": [".vscode/tailwind.json"],
"tailwindCSS.experimental.classRegex": [
"tw`([^`]*)",
["tw\\(([^\\)]*)\\)", "`([^`]*)`"]
["tw\\(([^\\)]*)\\)", "`([^`]*)`"],
"(?:enter|leave)(?:From|To)?=\\s*(?:\"|'|{`)([^(?:\"|'|`})]*)",
["clsx\\(.*?\\)(?!\\])", "(?:'|\"|`)([^\"'`]*)(?:'|\"|`)"]
],
"explorer.fileNesting.patterns": {
"*.mdx": "${capture}.js"
Expand Down
4 changes: 2 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@tailwindcss/typography": "npm:@tailwindcss/typography@0.5.15",
"@types/hast": "npm:@types/hast@3.0.4",
"@vendor/": "./vendor/",
"clsx": "npm:clsx@2.1.1",
"clsx": "npm:clsx@2.1.1/lite",
"idb-keyval": "npm:idb-keyval@6.2.1",
"openai": "https://deno.land/x/openai@v4.50.0/mod.ts",
"openai/": "https://deno.land/x/openai@v4.50.0/",
Expand Down Expand Up @@ -96,7 +96,7 @@
}
},
"compilerOptions": {
"jsx": "react-jsx",
"jsx": "precompile",
"jsxImportSource": "preact",
"strict": true,
"noFallthroughCasesInSwitch": true,
Expand Down
8 changes: 5 additions & 3 deletions src/components/Admonition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
IconFlame,
IconInfoCircle,
} from "@tabler/icons-preact";
import { clsx } from "clsx";
import type { JSX, RenderableProps } from "preact";
import { tw } from "../utils/tags.ts";

Expand Down Expand Up @@ -82,9 +83,10 @@ export function Admonition({
}: RenderableProps<AdmonitionProps>): JSX.Element {
return (
<div
class={`flex w-2/3 flex-col divide-y rounded-lg border-2 *:px-4 ${getAdmonitionStyles(
type,
)}`}
class={clsx(
"flex w-2/3 flex-col divide-y rounded-lg border-2 *:px-4",
getAdmonitionStyles(type),
)}
>
<div class="flex flex-row items-center *:mr-4">
<AdmonitionIcon type={type} /> {getTitle(type)}
Expand Down
10 changes: 5 additions & 5 deletions src/components/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ function createCarouselStyles(number: number): string {

const delayList = Array.from({ length: number })
.map((_, i) => {
const animationDelay =
(i * 2 * visibilityPercentage * totalAnimationTime) / 100;

return css`
&:nth-child(${i + 1}) {
animation-delay: ${
(i * 2 * visibilityPercentage * totalAnimationTime) / 100
}s;
}
`;
animation-delay: ${animationDelay}s;
}`;
})
.join("\n");
const delays = css`
Expand Down
34 changes: 10 additions & 24 deletions src/islands/Chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,30 @@ import {
import { chat } from "../sdk/chat/index.ts";
import { getThreadId } from "../sdk/chat/thread.ts";
import { setIndexedDb, useIndexedDb } from "../utils/hooks/indexeddb.ts";
import { constants as openAiConstants } from "../utils/openai/assistant.ts";
import { formatRefs } from "../utils/openai/references.ts";
import type { Message } from "../utils/openai/schemas.ts";
import { tw } from "../utils/tags.ts";
import { Markdown } from "./Markdown.tsx";

export function Chatbot(): JSX.Element {
if (openAiConstants === undefined) {
// This is also handled in the layout,
// but this stops us from shooting ourselves in the foot later,
// in case we change something.
return <></>;
}

const icon = <IconMessageChatbot class="size-8" />;
const buttonStyles = clsx(floatingButtonStyles, blueButtonStyles);

if (!IS_BROWSER) {
return (
<button type="button" class={buttonStyles}>
{icon}
</button>
);
}

return (
<Popover>
<PopoverButton className={buttonStyles} aria-label="Meet our Chatbot!">
<PopoverButton
className={clsx(floatingButtonStyles, blueButtonStyles)}
aria-label="Meet our Chatbot!"
>
{icon}
</PopoverButton>
<Transition
appear={true}
enter={tw`transition-opacity duration-75`}
enterFrom={tw`opacity-0`}
enterTo={tw`opacity-100`}
leave={tw`transition-opacity duration-150`}
leaveFrom={tw`opacity-100`}
leaveTo={tw`opacity-0`}
enter="transition-opacity duration-75"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<PopoverPanel>
<Suspense fallback={<Fragment />}>
Expand Down
33 changes: 7 additions & 26 deletions src/islands/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,29 @@
import { IS_BROWSER } from "$fresh/runtime.ts";
import { Field, Input, Label } from "@headlessui/react";
import { clsx } from "clsx";
import type { JSX } from "preact";
import { tw } from "../utils/tags.ts";

export interface CheckboxProps {
readonly name: string;
readonly labelText: string;
readonly required?: boolean;
}

const fieldStyles = tw`top-16 flex w-48 md:w-72 flex-col items-center gap-4`;
const labelStyles = tw`md:text-lg`;
// Removal of ring based on https://romansorin.com/blog/disabling-the-tailwind-input-ring
const inputStyles = tw`cursor-default rounded border-2 border-slate-500 bg-slate-200 shadow-md focus-visible:outline-none focus:ring-1 focus:ring-offset-0 focus-visible:border-blue-600 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-slate-800`;

export function Checkbox({
name,
labelText,
required,
}: CheckboxProps): JSX.Element {
if (!IS_BROWSER) {
return (
<div class={fieldStyles}>
<label class={labelStyles}>{labelText}</label>
<input
class={inputStyles}
aria-label={labelText}
type="checkbox"
name={name}
required={required}
disabled
/>
</div>
);
}

return (
<Field disabled={!IS_BROWSER} class={fieldStyles}>
<Label class={labelStyles}>{labelText}</Label>
<Field
disabled={!IS_BROWSER}
class="top-16 flex w-48 md:w-72 flex-col items-center gap-4"
>
<Label class="md:text-lg">{labelText}</Label>
<Input
name={name}
type="checkbox"
className={clsx(tw`form-checkbox`, inputStyles)}
// Removal of ring based on https://romansorin.com/blog/disabling-the-tailwind-input-ring
className="form-checkbox cursor-default rounded border-2 border-slate-500 bg-slate-200 shadow-md focus-visible:outline-none focus:ring-1 focus:ring-offset-0 focus-visible:border-blue-600 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-slate-800"
required={required}
/>
</Field>
Expand Down
16 changes: 7 additions & 9 deletions src/islands/HeaderGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { IS_BROWSER } from "$fresh/runtime.ts";
import { PopoverGroup } from "@headlessui/react";
import { clsx } from "clsx";
import type { JSX } from "preact";
import type { ComponentChildren } from "preact";
import { tw } from "../utils/tags.ts";

const listStyles = tw`flex flex-shrink flex-row flex-wrap items-center gap-6`;

export interface HeaderGroupProps {
readonly children?: ComponentChildren;
Expand All @@ -16,12 +12,14 @@ export function HeaderGroup({
children,
...props
}: HeaderGroupProps): JSX.Element {
if (!IS_BROWSER) {
return <ul class={listStyles}>{children}</ul>;
}

return (
<PopoverGroup as={"ul"} className={clsx(listStyles, props)}>
<PopoverGroup
as={"ul"}
className={clsx(
"flex flex-shrink flex-row flex-wrap items-center gap-6",
props,
)}
>
{children}
</PopoverGroup>
);
Expand Down
49 changes: 18 additions & 31 deletions src/islands/HeaderMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { IS_BROWSER } from "$fresh/runtime.ts";
import {
Popover,
PopoverButton,
Expand All @@ -10,7 +9,6 @@ import { clsx } from "clsx";
import type { JSX } from "preact";
import { prettyFocus } from "../components/styles.ts";
import type { Menu } from "../utils/site-organization.ts";
import { tw } from "../utils/tags.ts";

/**
* Make the text style for the menu.
Expand All @@ -19,11 +17,12 @@ import { tw } from "../utils/tags.ts";
* @returns The text style for the menu.
*/
function makeTextStyle(active: boolean): string {
return tw`py-1 hover:text-slate-700 data-[current]:font-bold dark:hover:text-slate-200 ${
return clsx(
"py-1 hover:text-slate-700 data-[current]:font-bold dark:hover:text-slate-200",
active
? tw`font-bold text-slate-700 dark:text-slate-200`
: tw`text-slate-500 dark:text-slate-400`
}`;
? "font-bold text-slate-700 dark:text-slate-200"
: "text-slate-500 dark:text-slate-400",
);
}

/**
Expand All @@ -33,11 +32,12 @@ function makeTextStyle(active: boolean): string {
* @returns The border style for the menu.
*/
function makeBorderStyle(active: boolean): string {
return tw` hover:border-slate-700 data-[current]:border-b-2 dark:hover:border-slate-200 ${
return clsx(
"hover:border-slate-700 data-[current]:border-b-2 dark:hover:border-slate-200",
active
? tw`border-b-2 border-slate-700 dark:border-slate-200`
: tw`border-slate-500 dark:border-slate-400`
}`;
? "border-b-2 border-slate-700 dark:border-slate-200"
: "border-slate-500 dark:border-slate-400",
);
}

/**
Expand All @@ -52,17 +52,13 @@ export interface WithActive {

export function menuButtonStyles(active: boolean): string {
return clsx(
tw`flex h-8 flex-row gap-0.5 whitespace-nowrap rounded-sm p-1 focus-visible:outline-none focus-visible:ring-1`,
"flex h-8 flex-row gap-0.5 whitespace-nowrap rounded-sm p-1 focus-visible:outline-none focus-visible:ring-1",
prettyFocus,
makeBorderStyle(active),
makeTextStyle(active),
);
}

function ButtonIcon(): JSX.Element {
return <IconChevronDown size={24} class="size-6" aria-hidden="true" />;
}

/**
* Render a dropdown menu component.
* It contains a header and a list of items.
Expand All @@ -81,28 +77,19 @@ export function HeaderMenu({
items,
active,
}: Menu & WithActive): JSX.Element {
if (!IS_BROWSER) {
return (
<div class={menuButtonStyles(active)}>
<div>{title}</div>
<ButtonIcon />
</div>
);
}

return (
<Popover>
<PopoverButton className={menuButtonStyles(active)}>
{title}
<ButtonIcon />
<IconChevronDown size={24} class="size-6" aria-hidden="true" />
</PopoverButton>
<Transition
enter={tw`transition ease-out duration-200`}
enterFrom={tw`opacity-0 translate-y-1`}
enterTo={tw`opacity-100 translate-y-0`}
leave={tw`transition ease-in duration-150`}
leaveFrom={tw`opacity-100 translate-y-0`}
leaveTo={tw`opacity-0 translate-y-1`}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<PopoverPanel
className="animate-anchor-gap z-30 origin-top-right rounded-md shadow-2xl"
Expand Down
17 changes: 0 additions & 17 deletions src/islands/Info.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { IS_BROWSER } from "$fresh/runtime.ts";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import { IconInfoCircle } from "@tabler/icons-preact";
import { clsx } from "clsx";
Expand All @@ -10,22 +9,6 @@ export interface InfoProps {
}

export function Info({ children }: InfoProps): JSX.Element {
if (!IS_BROWSER) {
return (
<button
type="button"
title="More Information"
class={clsx(
"inline-flex size-5 place-items-center rounded-full",
prettyFocus,
)}
>
<IconInfoCircle class="inline" size={20} />
<div class="hidden">{children}</div>
</button>
);
}

return (
<Popover as={Fragment}>
<PopoverButton
Expand Down
Loading
Loading