Fix issues

This commit is contained in:
Arthur Belleville 2025-10-23 23:27:13 +02:00
parent 9068772ca3
commit d120f43255
No known key found for this signature in database
9 changed files with 72 additions and 35 deletions

View file

@ -15,7 +15,6 @@
"deploy:main:staging": "turbo deploy:staging --filter=@xtablo/main",
"deploy:main:prod": "turbo deploy:prod --filter=@xtablo/main",
"deploy:external": "turbo deploy --filter=@xtablo/external",
"deploy:all": "pnpm deploy:main && pnpm deploy:external",
"lint": "turbo lint",
"lint:fix": "turbo lint:fix",
"format": "turbo format",

View file

@ -193,11 +193,11 @@ export const parseICSFile = (icsContent: string): ParsedICSEvent[] => {
};
for (let i = 0; i < lines.length; i++) {
let line = lines[i].trim();
let line = lines[i]?.trim() || "";
// Handle line folding (lines starting with space or tab)
while (i + 1 < lines.length && /^[ \t]/.test(lines[i + 1])) {
line += lines[i + 1].substring(1);
while (i + 1 < lines.length && /^[ \t]/.test(lines[i + 1] || "")) {
line += lines[i + 1]?.substring(1) || "";
i++;
}

View file

@ -26,6 +26,7 @@ function ButtonGroup({
...props
}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
return (
// biome-ignore lint/a11y/useSemanticElements: This is a UI grouping element, not a form fieldset
<div
role="group"
data-slot="button-group"

View file

@ -1,11 +1,52 @@
"use client";
import { cn } from "@xtablo/shared";
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon } from "lucide-react";
import * as React from "react";
import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
import { Button, buttonVariants } from "./button";
// Component definitions outside to avoid recreation on every render
const CalendarRoot = ({
className,
rootRef,
...props
}: React.HTMLAttributes<HTMLDivElement> & { rootRef?: React.Ref<HTMLDivElement> }) => {
return <div data-slot="calendar" ref={rootRef} className={cn(className)} {...props} />;
};
const CalendarChevron = ({
className,
orientation,
...props
}: React.SVGProps<SVGSVGElement> & { orientation?: "up" | "left" | "right" | "down" }) => {
switch (orientation) {
case "up":
return <ChevronUpIcon className={cn("size-4", className)} {...props} />;
case "left":
return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
case "right":
return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
case "down":
return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
default:
return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
}
};
const CalendarWeekNumber = ({
children,
...props
}: React.TdHTMLAttributes<HTMLTableCellElement>) => {
return (
<td {...props}>
<div className="flex size-(--cell-size) items-center justify-center text-center">
{children}
</div>
</td>
);
};
function Calendar({
className,
classNames,
@ -104,30 +145,10 @@ function Calendar({
...classNames,
}}
components={{
Root: ({ className, rootRef, ...props }) => {
return <div data-slot="calendar" ref={rootRef} className={cn(className)} {...props} />;
},
Chevron: ({ className, orientation, ...props }) => {
if (orientation === "left") {
return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
}
if (orientation === "right") {
return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
}
return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
},
Root: CalendarRoot,
Chevron: CalendarChevron,
DayButton: CalendarDayButton,
WeekNumber: ({ children, ...props }) => {
return (
<td {...props}>
<div className="flex size-(--cell-size) items-center justify-center text-center">
{children}
</div>
</td>
);
},
WeekNumber: CalendarWeekNumber,
...components,
}}
{...props}

View file

@ -113,6 +113,7 @@ export function DateFieldLabel({
"aria-required"?: boolean;
}) {
return (
// biome-ignore lint/a11y/noLabelWithoutControl: This label is for display only within a custom date picker component
<label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{children}
{ariaRequired && <span className="text-destructive ml-1">*</span>}

View file

@ -3,7 +3,7 @@
import { CalendarDate } from "@internationalized/date";
import { cn } from "@xtablo/shared";
import { Calendar as CalendarIcon, ChevronDownIcon } from "lucide-react";
import { useState } from "react";
import { useId, useState } from "react";
import { Button } from "./button";
import { Calendar } from "./calendar";
import { Label } from "./label";
@ -91,15 +91,16 @@ export const DatePickerV1 = ({
onChange: (date: Date | undefined) => void;
}) => {
const [open, setOpen] = useState(false);
const id = useId();
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor="date" className="px-1">
<Label htmlFor={id} className="px-1">
{label}
</Label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline" id="date" className="w-full justify-between font-normal">
<Button variant="outline" id={id} className="w-full justify-between font-normal">
{value ? value.toLocaleDateString() : "Choisir une date"}
<ChevronDownIcon />
</Button>

View file

@ -78,6 +78,7 @@ function Field({
...props
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
return (
// biome-ignore lint/a11y/useSemanticElements: This is a UI grouping element, not a form fieldset
<div
role="group"
data-slot="field"
@ -194,7 +195,10 @@ function FieldError({
return (
<ul className="ml-4 flex list-disc flex-col gap-1">
{errors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
{errors.map(
(error, index) =>
error?.message && <li key={`error-${error.message}-${index}`}>{error.message}</li>
)}
</ul>
);
}, [children, errors]);

View file

@ -6,7 +6,9 @@ export function useCopyToClipboard({ timeout = 2000 } = {}) {
const [copyTimeout, setCopyTimeout] = React.useState<number | null>(null);
const handleCopyResult = (value: boolean) => {
window.clearTimeout(copyTimeout!);
if (copyTimeout !== null) {
window.clearTimeout(copyTimeout);
}
setCopyTimeout(window.setTimeout(() => setCopied(false), timeout));
setCopied(value);
};
@ -18,14 +20,18 @@ export function useCopyToClipboard({ timeout = 2000 } = {}) {
.then(() => handleCopyResult(true))
.catch((err) => setError(err));
} else {
setError(new Error("useCopyToClipboard: navigator.clipboard is not supported"));
setError(
new Error("useCopyToClipboard: navigator.clipboard is not supported")
);
}
};
const reset = () => {
setCopied(false);
setError(null);
window.clearTimeout(copyTimeout!);
if (copyTimeout !== null) {
window.clearTimeout(copyTimeout);
}
};
return { copy, reset, error, copied };

View file

@ -22,6 +22,10 @@
"inputs": ["src/**", "biome.json", "package.json"],
"outputLogs": "new-only"
},
"lint:fix": {
"inputs": ["src/**", "biome.json", "package.json"],
"outputLogs": "new-only"
},
"format": {
"inputs": ["src/**", "biome.json", "package.json"],
"outputLogs": "errors-only"