feat: some shit
This commit is contained in:
95
components/Checkbox.tsx
Normal file
95
components/Checkbox.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import { Signal, useSignal } from "@preact/signals";
|
||||
import { useId, useState } from "preact/hooks";
|
||||
|
||||
interface CheckboxProps {
|
||||
label: string;
|
||||
isChecked?: boolean;
|
||||
onChange: (isChecked: boolean) => void;
|
||||
}
|
||||
|
||||
const Checkbox2: preact.FunctionalComponent<CheckboxProps> = (
|
||||
{ label, isChecked = false, onChange },
|
||||
) => {
|
||||
const [checked, setChecked] = useState(isChecked);
|
||||
|
||||
const toggleCheckbox = () => {
|
||||
const newChecked = !checked;
|
||||
setChecked(newChecked);
|
||||
onChange(newChecked);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
class="flex items-center rounded-xl p-1 pl-4"
|
||||
style={{ background: "var(--background)", color: "var(--foreground)" }}
|
||||
>
|
||||
<span>
|
||||
{label}
|
||||
</span>
|
||||
<label
|
||||
class="relative flex cursor-pointer items-center rounded-full p-3"
|
||||
for="checkbox"
|
||||
data-ripple-dark="true"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-blue-gray-200 transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity hover:before:opacity-10"
|
||||
id="checkbox"
|
||||
checked
|
||||
/>
|
||||
<div
|
||||
class={`pointer-events-none absolute top-2/4 left-2/4 -translate-y-2/4 -translate-x-2/4 text-white opacity-${
|
||||
checked ? 100 : 0
|
||||
} transition-opacity`}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3.5 w-3.5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="white"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clip-rule="evenodd"
|
||||
>
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Checkbox = (
|
||||
{ label, checked = useSignal(false) }: {
|
||||
label: string;
|
||||
checked?: Signal<boolean>;
|
||||
},
|
||||
) => {
|
||||
const _id = useId();
|
||||
const id = `checkbox-${_id}`;
|
||||
return (
|
||||
<label
|
||||
className="flex items-center py-3 px-4 rounded-xl"
|
||||
style={{ color: "var(--foreground)", background: "var(--background)" }}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked.value}
|
||||
name="checkbox-one"
|
||||
id={id}
|
||||
onChange={(ev) => {
|
||||
checked.value = ev.currentTarget.checked;
|
||||
}}
|
||||
class="bg-gray-200 hover:bg-gray-300 cursor-pointer
|
||||
w-5 h-5 border-3 border-amber-500 focus:outline-none rounded-lg"
|
||||
/>
|
||||
<span class="ml-3">{label}</span>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
export default Checkbox;
|
14
components/Image.tsx
Normal file
14
components/Image.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { isLocalImage } from "@lib/string.ts";
|
||||
|
||||
export function Image(
|
||||
props: { class: string; src: string; width?: number; height?: number },
|
||||
) {
|
||||
if (isLocalImage(props.src)) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<img src={props.src} width={props.width} height={props.height} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { Card } from "@components/Card.tsx";
|
||||
import { Movie } from "@lib/resource/movies.ts";
|
||||
import { Series } from "@lib/resource/series.ts";
|
||||
|
||||
export function MovieCard({ movie }: { movie: Movie }) {
|
||||
const { meta: { image = "/placeholder.svg" } = {} } = movie;
|
||||
@ -16,3 +17,19 @@ export function MovieCard({ movie }: { movie: Movie }) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function SeriesCard({ series }: { series: Series }) {
|
||||
const { meta: { image = "/placeholder.svg" } = {} } = series;
|
||||
|
||||
const imageUrl = image?.startsWith("Media/series/")
|
||||
? `/api/images?image=${image}&width=200&height=200`
|
||||
: image;
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={series.name}
|
||||
image={imageUrl}
|
||||
link={`/series/${series.id}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
34
components/Rating.tsx
Normal file
34
components/Rating.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { IconStar, IconStarFilled } from "@components/icons.tsx";
|
||||
import { useSignal } from "@preact/signals";
|
||||
import { useState } from "preact/hooks";
|
||||
|
||||
export const Rating = (
|
||||
props: { max?: number; rating: number },
|
||||
) => {
|
||||
const [rating, setRating] = useState(props.rating);
|
||||
const [hover, setHover] = useState(0);
|
||||
const max = useSignal(props.max || 5);
|
||||
|
||||
return (
|
||||
<div
|
||||
class="flex gap-2 px-5 rounded-2xl bg-gray-200 z-10"
|
||||
style={{ color: "var(--foreground)", background: "var(--background)" }}
|
||||
>
|
||||
{Array.from({ length: max.value }).map((_, i) => {
|
||||
return (
|
||||
<span
|
||||
class={`my-5 cursor-pointer opacity-${
|
||||
(i + 1) <= rating ? 100 : (i + 1) <= hover ? 20 : 100
|
||||
}`}
|
||||
onMouseOver={() => setHover(i + 1)}
|
||||
onClick={() => setRating(i + 1)}
|
||||
>
|
||||
{(i + 1) <= rating || (i + 1) <= hover
|
||||
? <IconStarFilled class="w-4 h-4" />
|
||||
: <IconStar class="w-4 h-4" />}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
Reference in New Issue
Block a user