Skip to content

Commit

Permalink
create details ui component
Browse files Browse the repository at this point in the history
  • Loading branch information
JulienR1 committed Nov 30, 2023
1 parent a223121 commit da5cb8a
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
104 changes: 104 additions & 0 deletions web/src/modules/ui/details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { A } from "@solidjs/router";
import { Component, For, Match, Show, Switch, ValidComponent } from "solid-js";
import { Dynamic } from "solid-js/web";
import { z } from "zod";
import { Icon } from "./icon";

type Linkify<T extends Record<string, unknown>> = Partial<{
[K in keyof T]: T[K] extends Record<string, unknown> ? Linkify<T[K]> : string;
}>;

type DetailsProps<T extends Record<string, unknown>> = {
header?: string[];
data: T;
links?: Linkify<T>;
depth?: number;
};

export const Details = <T extends Record<string, unknown>>(
props: DetailsProps<T>,
) => {
return (
<ul>
<DetailsHeader header={props.header} />
<For each={Object.entries(props.data)}>
{([key, value]) => (
<DetailsRow
label={key}
value={value}
links={props.links?.[key]}
depth={props.depth ?? 0}
/>
)}
</For>
</ul>
);
};

// TODO
const DetailsHeader: Component<{ header?: string[] }> = (props) => {
return (
<Show when={(props.header ?? []).length > 0}>
<li>
<For each={props.header}>{(heading) => <p>{heading}</p>}</For>
</li>
</Show>
);
};

type DetailsRowProps = {
label: string;
value: unknown;
depth: number;
links?: Record<string, unknown> | string;
};

const DetailsRow: Component<DetailsRowProps> = (props) => {
const hasLink = typeof props.links === "string";

const Inner = () => (
<div class="grid grid-cols-[125px_auto] rounded px-2 py-1">
<span class="flex items-center gap-2 font-semibold">
{props.label}
<Show when={typeof props.links === "string"}>
<Icon name="open_in_new" size="sm" />
</Show>
</span>
<div class="relative w-fit after:absolute after:bottom-0 after:left-0 after:block after:h-[2px] after:w-[calc(100%+32px)] after:-translate-x-4 after:bg-primary">
<Switch fallback={<span>{String(props.value)}</span>}>
<Match when={typeof props.value === "function"}>
<Dynamic component={props.value as ValidComponent} />
</Match>

<Match
when={
z.record(z.string(), z.unknown()).safeParse(props.value).success
}
>
<Details
data={props.value as Record<string, unknown>}
links={(props.links as any)?.[props.label]}
depth={props.depth + 1}
/>
</Match>
</Switch>
</div>
</div>
);

return (
<li
class="font-light hocus:bg-gray-100"
classList={{
"text-sm": props.depth == 1,
"text-xs": props.depth > 1,
}}
>
<Show when={hasLink} fallback={<Inner />}>
<A href={props.links as string} class="hocus:font-medium">
<Inner />
</A>
</Show>
</li>
);
};
1 change: 1 addition & 0 deletions web/src/modules/ui/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./accordion";
export * from "./backdrop";
export * from "./card";
export * from "./details";
export * from "./icon";
export * from "./no-content";
export * from "./skeleton";
Expand Down
5 changes: 5 additions & 0 deletions web/src/resources/formatters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const dateFormatter = new Intl.DateTimeFormat("fr-CA", {});
export const moneyFormatter = new Intl.NumberFormat("fr-CA", {
style: "currency",
currency: "CAD",
});

0 comments on commit da5cb8a

Please sign in to comment.